[macOS] Overlapping Appshots leave a full-screen transition overlay stuck because cancellation is not propagated
What version of the Codex App are you using (From “About Codex” dialog)?
ChatGPT/Codex Desktop 26.715.21425 (CFBundleVersion 5488)
Computer Use helper:
CFBundleIdentifier: com.openai.sky.CUAService
CFBundleShortVersionString: 26.715.1000451
CFBundleVersion: 1000451
Distribution hashes:
app.asar:
5db4c67090c0521fa717e83e46cb0a6175cb6c16fb89064223753bdf05cff0aa
SkyComputerUseService:
44320516c4c400fb5459b203498c78e4af318b0096464f16c4445a47f2b8b8f4
What subscription do you have?
ChatGPT Pro
What platform is your computer?
Darwin 25.5.0 arm64 arm
macOS 26.5 (25F71)
Apple Silicon
What issue are you seeing?
If a second Appshot is started while an earlier Appshot is still pending, the later capture can complete successfully while the first capture's full-screen transition overlay remains above every application.
The stuck window is a native layer-25, full-display window whose class is:
Appshot.AppshotCaptureTransitionOverlayWindow
It displays the captured application image above all windows. The keyboard and underlying applications continue to run, but the overlay obscures the desktop until SkyComputerUseService is terminated or the native capture eventually unwinds.
This is not simply a failure to attach multiple completed images. The trigger is overlapping captures with distinct request IDs when the older request has emitted a screenshot but has not emitted axText or completed.
Observed request timeline
The following timestamps are UTC from ~/Library/Logs/com.openai.codex:
21:02:08.324 Mail capture created
requestId=89221d90-ebfe-4885-bee7-c340dce41a37
21:02:09.017 Mail update: metadata
21:02:09.218 Mail update: screenshot
No axText or completed update followed.
21:02:14.112 Safari capture created
requestId=d0a99d24-4abc-45bd-81d7-8db5df888363
21:02:14.732 Safari update: metadata
21:02:14.764 Safari update: screenshot
21:02:14.860 Safari update: axText
21:02:15.190 Safari update: completed / status=success
The Mail transition overlay remained after Safari completed. After manually terminating the exact Computer Use service process, the Mail update watcher returned:
AESendMessage failed with -600
and the app finally settled the Mail request as discarded after 70.641 seconds.
A previous occurrence reached the app's 120-second timeout:
Timed out waiting for Codex Computer Use capture
elapsedMs=120835
hadAxText=false
hadScreenshot=true
status=discarded
but the native overlay remained visible well beyond the JavaScript timeout.
Installed-build implementation findings
The desktop implementation does not appear to be present in the public repository, so these locations refer to the installed distribution. The ASAR does not include its referenced source maps.
In app.asar/.vite/build/main-hw0RxS4P.js, raw line 366:
- The pending-capture collection is
FO = new Map()at byte offset 511678. - The capture-start handler
VO()begins at byte offset 511839. - It rejects only
FO.has(requestId), meaning duplicate requests with the same ID. - It does not reject a new request when another request ID is already pending and does not cancel/supersede the older request.
UO()at byte offset 513842 deletes the map entry, clears the timer, callsAbortController.abort(), and unsubscribes.GO()at byte offset 514107 implements the 120-second timeout, but the cleanup does not invoke a native capture/transition cancellation operation.
In app.asar/.vite/build/worker.js:
- The Computer Use capture worker begins near raw line 182, byte offset 1342319.
- Its cancellation handler at byte offset 1343701 is exactly:
handleCancel() {}
- The native bridge at raw line 206 exposes only:
ComputerUseIPCAppStartCaptureRequest
ComputerUseIPCAppNextCaptureUpdateRequest
No capture-cancel Apple Event request is present.
The native SkyComputerUseService binary does contain:
ComputerUse.AppshotCaptureStore.captures
Appshot.AppshotCaptureTransition.start()
Appshot.AppshotCaptureTransition.complete()
Appshot.AppshotCaptureTransition.cancel()
Appshot.AppshotCaptureTransitionOverlayWindow
AppshotCaptureTransition.cancel() is present at VM address 0x100ca5138 in this binary. Its implementation iterates the overlay windows and closes/removes them, but there is no ComputerUseIPCAppCancelCaptureRequest path from Electron to reach it.
During a stuck capture, a process sample showed the capture work blocked in synchronous Accessibility calls including:
AXUIElementCopyAttributeValue
AXUIElementCopyAttributeNames
AXUIElementCopyMultipleAttributeValues
The service imports those AX copy APIs but does not import/use AXUIElementSetMessagingTimeout.
Taken together, the failure path appears to be:
- The first capture creates and starts a native transition overlay.
- AX extraction stalls after the screenshot update.
- A second request ID is accepted and completes independently.
- Completing the second transition does not cancel the first transition.
- JavaScript abort/timeout disposes listeners and workers, but
handleCancel()is empty and no native cancel request exists. - The first native transition therefore retains its full-screen overlay.
What steps can reproduce the bug?
- Open Codex Desktop on macOS.
- Bring Mail to the foreground.
- Trigger an Appshot using the Double-Command shortcut.
- Before the first capture reaches
axText/completed, switch to Safari. - Trigger another Appshot.
- Observe that the Safari Appshot completes and is attached.
- Observe that the earlier full-screen Appshot transition remains above every application.
- Terminating only
SkyComputerUseServiceclears the overlay and causes the pending update poll to fail with Apple Event error-600.
The timing depends on the first target's AX traversal stalling, but the same overlapping request shape has now been observed more than once.
What is the expected behavior?
Appshot capture should either be single-flight or explicitly supersede/cancel the previous capture.
Regardless of whether screenshot capture, AX extraction, update polling, or attachment fails:
- no full-screen transition overlay should survive the request;
- a later Appshot should not leave an older transition active;
- the 120-second timeout should cancel the corresponding native capture, not only discard JavaScript state.
Additional information
Potential fix points:
- Add a single-flight guard in the main-process capture manager, or explicitly cancel all prior pending request IDs before accepting a new one.
- Implement the Computer Use worker's
handleCancel(requestId). - Add a native cancel Apple Event/IPC request that removes the request from
AppshotCaptureStoreand callsAppshotCaptureTransition.cancel(). - Put transition completion/cancellation in a guaranteed cleanup path so every error and cancellation closes the overlay.
- Bound AX traversal with
AXUIElementSetMessagingTimeout, task cancellation, or a screenshot-only fallback when AX text cannot be collected. - Preserve the native/worker
failureReason; in this reproduction the final renderer log reportedfailureReason=undefinedeven though the worker emittedupdate_poll_failed.
I can provide additional redacted logs or a controlled process sample if needed.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗