Windows 10 Computer Use deadlocks awaiting SoftwareBitmap conversion inside FrameArrived
What version of the Codex App are you using (From “About Codex” dialog)?
26.721.4979.0
What subscription do you have?
ChatGPT Plus
What platform is your computer?
Microsoft Windows NT 10.0.19045.0 x64
What issue are you seeing?
On Windows 10 22H2 build 19045, Computer Use screenshot capture still times out after the unsupported IsBorderRequired call is skipped.
Windows.Graphics.Capture starts successfully and delivers a real FrameArrived event. TryGetNextFrame returns a valid Direct3D11CaptureFrame and IDirect3DSurface. The helper then synchronously waits for SoftwareBitmap.CreateCopyFromSurfaceAsync inside the CreateFreeThreaded FrameArrived callback.
The async operation accepts its completion handler with HRESULT 0x00000000, but the completion handler is never invoked while FrameArrived remains blocked. The outer request eventually reports:
FrameArrived timed out: timed out waiting on channel
This is downstream of the Windows 10 IsBorderRequired compatibility problem reported in #25178. Ignoring E_NOINTERFACE exposes this second independent failure.
What steps can reproduce the bug?
- Run Codex Desktop 26.721.4979.0 with Computer Use plugin 26.721.41059 on Windows 10 22H2 build 19045.
- Start Computer Use and request a screenshot of a normal window such as Notepad.
- Skip
E_NOINTERFACE(0x80004002) fromGraphicsCaptureSession.IsBorderRequiredso initialization continues. CreateFreeThreaded, event registration, session creation, andStartCapturesucceed.FrameArrivedenters on a WGC worker thread.TryGetNextFramesucceeds andSurfaceis valid.CreateCopyFromSurfaceAsyncreturns an operation andput_CompletedreturnsS_OK.- The completion handler is never invoked while
FrameArrivedsynchronously waits. - The screenshot request times out waiting for its result channel.
A standalone WGC probe confirmed that both CreateFreeThreaded and Create + DispatcherQueue continuously produce valid frames on this machine. The stall reproduced only when CreateCopyFromSurfaceAsync(...).get() ran synchronously inside FrameArrived.
It reproduced with both a Rust/windows-rs-style agile COM delegate and a standard C++/WinRT delegate, excluding the delegate ABI as the cause.
What is the expected behavior?
FrameArrived should return without synchronously waiting for SoftwareBitmap conversion.
The helper should retain the frame or surface, return from the callback, and perform SoftwareBitmap conversion and JPEG encoding on a dedicated MTA worker before notifying the existing result channel.
The validated probe used this structure and conversion completed successfully in about 12.9 ms while WGC continued producing valid frames.
This does not require switching to DispatcherQueue or replacing Windows.Graphics.Capture.
Additional information
Environment:
- Windows 10 22H2, build 19045
- Codex Desktop: 26.721.4979.0
- Computer Use plugin: 26.721.41059
@oai/sky: 0.5.2- Native helper: x64 Rust/MSVC
codex-computer-use.exe - Rust Windows bindings:
windows 0.61.3,windows-core 0.61.2 - GPU: AMD Radeon RX 6650 XT
Dynamic evidence for this helper build:
FrameArrived handler Invoke: RVA 0xC17FC
valid Surface path: RVA 0xC1993
CreateCopy async operation created: RVA 0xC1D69
async completion registration: RVA 0xC1F19
put_Completed call: RVA 0xC1F59
put_Completed return: RVA 0xC1F5C
async completion handler Invoke: RVA 0x1268BC
conversion result path: RVA 0xC2053
result/channel notification: RVA 0xC4CC0
Observed: put_Completed returns S_OK, but the completion handler, conversion result path, and result-channel notification are never reached.
Validated worker result:
WGC callback thread: 11656
conversion worker: 20544
conversion completed: true
conversion latency: 12.865 ms
FrameArrived count: 240
valid frame count: 240
Suggested fix:
- Call
TryGetNextFramein the callback. - Move
Direct3D11CaptureFrameto a bounded worker queue. - Return from
FrameArrivedimmediately. - On a dedicated MTA worker, obtain
Surface, awaitCreateCopyFromSurfaceAsync, encode JPEG, and notify the existing channel. - Preserve the current one-result guard, timeout, cancellation, and late-result handling.