Windows Computer Use screenshot fails on Windows 10 22H2 when SetIsBorderRequired is called

Open 💬 17 comments Opened May 30, 2026 by Define1165250535
💡 Likely answer: A maintainer (darlingm, contributor) responded on this thread — see the highlighted reply below.

Summary

On Windows Codex Desktop, Computer Use can list apps/windows, activate windows, read accessibility text, and send keyboard input, but any get_window_state call that requests a screenshot fails before capture with:

SetIsBorderRequired failed: 不支持此接口 (0x80004002)

The same target window works when requesting accessibility text only (include_screenshot: false, include_text: true). This looks like the Windows capture helper unconditionally calls GraphicsCaptureSession.IsBorderRequired / SetIsBorderRequired on a Windows build that does not support that property.

Environment

Sanitized user machine details:

  • Platform: Windows Codex Desktop
  • Codex app package observed: OpenAI.Codex_26.527.3686.0_x64__2p2nqsd0c76g0
  • Computer Use plugin observed: computer-use/26.527.31326
  • OS: Windows 10 Pro 22H2
  • OS build: 10.0.19045
  • GPU: NVIDIA GeForce GTX 1060 3GB
  • DirectX: 12
  • Driver model: WDDM 2.7
  • Remote/virtual display drivers also present: ToDesk Virtual Display Adapter, Oray Display Mirror Driver

Reproduction

From the Computer Use client in Codex Desktop on this Windows 10 machine:

  1. Bootstrap Computer Use and call sky.list_apps().
  2. Open or select a simple window such as Notepad.
  3. Activate the window.
  4. Call any screenshot-including state request, for example:
await sky.get_window_state({ window: targetWindow });
// or
await sky.get_window_state({ window: targetWindow, include_screenshot: true, include_text: false });
// or
await sky.get_window_state({ window: targetWindow, include_screenshot: true, include_text: true });
  1. The call fails with:
SetIsBorderRequired failed: 不支持此接口 (0x80004002)
  1. Calling text-only succeeds:
await sky.get_window_state({ window: targetWindow, include_screenshot: false, include_text: true });

Observed text-only result against Notepad included a focused editor element and accessibility tree, proving that the helper, native pipe, app enumeration, activation, and accessibility path are otherwise working.

Observed Batch Test Results

{
  "notepad_default_screenshot": "failed: SetIsBorderRequired failed: 不支持此接口 (0x80004002)",
  "notepad_explicit_screenshot": "failed: SetIsBorderRequired failed: 不支持此接口 (0x80004002)",
  "notepad_text_only": "ok",
  "notepad_both": "failed: SetIsBorderRequired failed: 不支持此接口 (0x80004002)",
  "explorer_screenshot": "failed: SetIsBorderRequired failed: 不支持此接口 (0x80004002)"
}

Suspected Cause

Microsoft documents GraphicsCaptureSession.IsBorderRequired as introduced in Windows 10 version 2104 / build 10.0.20348.0, API contract v12:

https://learn.microsoft.com/en-us/uwp/api/windows.graphics.capture.graphicscapturesession.isborderrequired

This repro machine is Windows 10 build 19045, so SetIsBorderRequired appears to hit E_NOINTERFACE (0x80004002, no such interface supported). The capture helper seems to abort instead of continuing without border toggling.

Expected Behavior

On Windows builds where border toggling is unsupported, Computer Use should still capture screenshots if the underlying Windows Graphics Capture path is otherwise available. Suggested behavior:

  • Gate the border call with ApiInformation.IsPropertyPresent("Windows.Graphics.Capture.GraphicsCaptureSession", "IsBorderRequired"), or an equivalent OS/API contract check.
  • If unsupported, skip SetIsBorderRequired and allow the default capture border behavior.
  • Alternatively, fail with a clear compatibility error such as: Computer Use screenshots require Windows build 20348+ because border toggling is currently unconditional.

Impact

This leaves Computer Use partially functional on Windows 10 22H2:

  • Working: app/window enumeration, activation, accessibility text, keyboard input.
  • Broken: screenshot-based UI inspection and accurate coordinate-based visual interaction.

For users on Windows 10 build 19045, this means Computer Use cannot reliably operate visual apps even though non-visual operations work.

View original on GitHub ↗

17 Comments

darlingm contributor · 1 month ago

Me too. Codex says this affects all Windows 10 users and it thinks OpenAI can easily fix it in the codex-computer-use.exe source file src\capture\image.rs, treating SetIsBorderRequired as optional. See feedback ID 019e779c-55f4-7113-a4fb-afcbc097c81b.

Codex says the IsBorderRequired functionality was introduced in Windows build 10.0.20348.0. It says the leading 10.0 is the Windows NT platform, not the marketing name, so it doesn't mean Windows 10. It says the most recent Windows 10 is 22H2 10.0.19045.x, and that 10.0.20348.0 refers to Widnows Server 2022.

So, it says there's no Windows 10 updates to add IsBorderRequired functionality.

One fix is to upgrade to Windows 11, but Codex thinks this can be fixed so Windows 10 is supported for this who can't or don't want to upgrade to it. It says SetIsBorderRequired isn't required to screenshot a program, and it controls whether Windows shows the colored capture border around a window/display being captured. When Codex calls it, it gets a E_NOINTERFACE / 0x80004002 and Codex aborts the whole screenshot operation.

It says if the IsBorderRequired system doesn't exist, or if calling it gives E_NOINTERFACE, that it should continue the screenshot capture with the default border behavior.

Like the original poster, computer use seems to be working for other functionality, just not when it needs to do screenshots.

darlingm contributor · 1 month ago

Also, Codex is able to manually do screenshots and see that just fine since it skips calling the function that isn't supported, but it says it can't use that screenshot because the helper guards coordinate input with checks including requiring calling get_window_state before issuing coordinate input, and that a screenshotId which has to come through the helper, not it manually screenshotting.

If I tell it to take the next step and issue the click/drags on its own, it belligerently tells me about how the Computer Use API has instructions prohibit it from falling back to raw UI automation when Computer Use is available but partially broken. Even when I remind it about the scene where the Terminator lets go of John, making John fall on the ground and realize the Terminator has to do what he says... It refuses to listen to me over its instructions.

BaseInfinity · 1 month ago

Adding another sanitized repro from a current Windows 10 machine.

Environment:

  • OS: Windows 10 Home, version 10.0.19045, 64-bit
  • Computer Use plugin cache observed: 26.601.20914
  • Codex app package version: not available from this shell because Get-AppxPackage cannot load the Appx module here

Observed behavior:

  • Computer Use can start and enumerate visible apps/windows.
  • Accessibility/text-only inspection can return useful UI text for some windows.
  • Screenshot-backed state/input fails with the English error:
SetIsBorderRequired failed: No such interface supported (0x80004002)

Impact in my repro:

  • Native Microsoft 365 Copilot, Excel, and Word windows could be found/foregrounded or inspected through limited accessibility paths, but screenshot-backed UI proof and coordinate input were blocked.
  • A direct Windows UIA fallback and local foreground screenshots could prove some visible Office/native-Copilot evidence, but that does not unblock Computer Use screenshot/coordinate workflows because the helper still requires its own screenshot state.

This lines up with the proposed root cause here: Windows 10 build 19045 does not expose the GraphicsCaptureSession.IsBorderRequired / SetIsBorderRequired API surface, so the helper should skip the optional border call or return an explicit compatibility message while keeping non-screenshot operations available.

qspkugsm · 1 month ago

I can confirm a second independent repro of this issue on Windows 10 22H2.

Environment

  • OS: Windows 10 Home 22H2
  • OS build: 10.0.19045 / build 19045
  • Codex desktop app package observed in logs: OpenAI.Codex_26.601.2237.0_x64__2p2nqsd0c76g0
  • Computer Use plugin: 26.601.21317
  • @oai/sky: 0.4.5-26566467949-4daa3290021d
  • Computer Use native helper process starts successfully.

Repro results

I tested sky.get_window_state against multiple windows:

  • Notepad (notepad.exe)
  • 360ChromeX
  • BaiduNetdiskUnite

For all three windows:

await sky.get_window_state({
  window,
  include_screenshot: false,
  include_text: true,
});

succeeds and returns an accessibility tree.

But any screenshot-including call fails consistently:

await sky.get_window_state({
  window,
  include_screenshot: true,
  include_text: false,
});

await sky.get_window_state({
  window,
  include_screenshot: true,
  include_text: true,
});

with:

SetIsBorderRequired failed: 不支持此接口 (0x80004002)

Because the plugin screenshot state is unavailable, coordinate clicking also becomes unusable and reports:

call get_window_state before issuing coordinate input

Additional check

A separate OS-level screenshot fallback using CopyFromScreen can capture the same Notepad and 360ChromeX windows on this machine, so this does not appear to be a general Windows screenshot/display failure. The failure seems isolated to the Computer Use Windows Graphics Capture path and its unconditional SetIsBorderRequired call on Windows build 19045.

This matches the suspected root cause described above: Microsoft documents GraphicsCaptureSession.IsBorderRequired as introduced in 10.0.20348.0, while Windows 10 22H2 desktop remains on 10.0.19045.

darlingm contributor · 1 month ago

If only Codex App was open source, we could just disable the call ourselves. #10733.

dajinglingpake · 1 month ago

I can reproduce this on my machine as well.

Environment:

  • OS: Windows 10 Pro, 10.0.19045.0, x64
  • Codex App package: OpenAI.Codex_26.602.3474.0_x64__2p2nqsd0c76g0
  • Codex.exe ProductVersion/FileVersion: 149.0.7827.54
  • Computer Use plugin directory version: 26.602.30954

Repro observed in Codex Desktop:

  1. Ask Codex to control a Windows desktop app such as Notepad or Paint.
  2. Computer Use can list the app and attempts to launch it.
  3. When it tries to capture/get the window state with screenshot support, it fails with:
SetIsBorderRequired failed: 不支持此接口 (0x80004002)

Impact:

  • Chrome Extension browser control works.
  • Desktop app control is blocked because Codex cannot capture the target app window and therefore cannot safely click/draw/type based on the visible state.

This looks consistent with a Windows 10 build 19045 compatibility issue around SetIsBorderRequired / GraphicsCaptureSession.IsBorderRequired. Feature-detecting that API and skipping it on unsupported Windows 10 builds would likely unblock desktop app control.

leua61 · 1 month ago

please fix this

Alitiantian · 1 month ago

Additional sanitized repro on a newer Codex Desktop build.

Environment:

  • Codex Desktop: 26.602.40724
  • Windows: 10.0.19045
  • Runtime observed: Electron/Chrome 149.0.7827.54
  • Computer Use helper/cache observed: computer-use/26.602.30954
  • WSL-backed Codex Desktop conversation

What works:

  • Computer Use connects to the Windows helper.
  • sky.list_apps() works.
  • sky.launch_app(...), sky.get_window(...), and sky.activate_window(...) work with Notepad.
  • include_text: true can return UIA/accessibility text in some windows.

What fails:
Calling screenshot capture for Notepad fails consistently:

await sky.get_window_state({
  window: notepadWindow,
  include_screenshot: true,
  include_text: false,
});

Observed error:

SetIsBorderRequired failed: 不支持此接口 (0x80004002)

Impact:
This leaves Computer Use limited to non-visual operations. Visual inspection, coordinate clicking, dragging, scrolling, and normal screen-state verification are blocked because screenshot state cannot be established.

This reproduces with Notepad only; no third-party or private application is needed. The behavior still looks consistent with the helper calling GraphicsCaptureSession.IsBorderRequired / SetIsBorderRequired on Windows 10 build 19045, where that API/property appears unsupported.

darlingm contributor · 1 month ago

Since Windows is supposed to be officially supported now, it would be great for there to be a Windows VM smoke test before releasing new app/plugin versions to make sure it at least launches and can do basic functionality. Windows support was announced but is definitely not a first-class feature right now.

kurutah · 1 month ago

Any news? :(

v7deep · 1 month ago

Windows Computer Use capture/input recovery

Date: 2026-06-18

Target tested:

  • Codex Computer Use plugin: openai-bundled/computer-use/26.611.61753
  • Helper binary: codex-computer-use.exe
  • Helper path: %LOCALAPPDATA%\OpenAI\Codex\runtimes\cua_node\<runtime-id>\bin\node_modules\@oai\sky\bin\windows\codex-computer-use.exe
  • Patched helper SHA-256: DFECD6E73B5D6EFA2383CEB1F73BFB4AEA02769CBE4481CD29711FFA79B8A348
  • Patched JS client SHA-256: 6995DDE1A975CA2E0581CC96A46E23F01DEC8ABF00ED31052E3533FBCFA2A4AC
  • Observed display scale: 125 percent DPI scaling, where GDI screenshots returned physical window pixels and native input consumed logical coordinates.

This document describes a local field patch used to restore Windows Computer Use enough to control Microsoft Paint and draw using click, drag, and screenshot verification. The binary offsets below are diagnostic/workaround material for this exact build layout, not a recommended upstream patch strategy.

Summary

Several independent failures blocked Windows Computer Use:

  1. Window capture failed when GraphicsCaptureSession.SetIsBorderRequired failed.
  2. After bypassing that failure, Windows Graphics Capture could still deadlock or time out while synchronously waiting for SoftwareBitmap.CreateCopyFromSurfaceAsync inside/near the frame callback path.
  3. Native input failed on GetCursorPos with access denied (0x80070005).
  4. Native input sometimes used corrupt or stale window bounds, producing impossible widths such as negative or very large values.
  5. The GDI screenshot fallback returned physical pixels, while the native input path expected logical coordinates, so high-level screenshot coordinates were off by the DPI scale factor.

The successful local recovery used:

  • A JS client fallback screenshot path using PrintWindow(PW_RENDERFULLCONTENT) with GetWindowDC/BitBlt fallback.
  • Native click/drag routed through the existing native pipe rather than ad hoc foreground input.
  • A DPI conversion layer before sending high-level screenshot coordinates to native click/drag.
  • Runtime hooks for GetCursorPos and bounds repair.
  • A few binary branch patches to bypass non-fatal capture/input checks while debugging.

Reproduction symptoms

Typical failures observed from the helper:

SetIsBorderRequired failed
FrameArrived timed out
SoftwareBitmap.CreateCopyFromSurfaceAsync failed
GetCursorPos failed: Access is denied. (0x80070005)
point (...) is outside window bounds { originX: 0, originY: 0, width: -1505865392, height: 699 }
point (...) is outside window bounds { originX: 0, originY: 0, width: 535975632, height: 436 }

After the fixes, a normal Paint workflow succeeded:

  1. Resolve the Paint window using list_apps() and get_window().
  2. Capture a screenshot with get_window_state().
  3. Select Paint brush and colors through click.
  4. Draw strokes through drag.
  5. Verify with a final screenshot.

Investigation notes

Capture path

The first blocker was SetIsBorderRequired failed. Treating this as fatal prevents capture on systems where this optional visual border setting is unavailable or denied. Bypassing this check allowed capture setup to proceed further.

The next blocker was in the frame path. Instrumentation around FrameArrived, TryGetNextFrame, and SoftwareBitmap.CreateCopyFromSurfaceAsync showed that a synchronous wait in the copy path could time out even though completion-related events happened immediately after the frame callback returned. This is consistent with a callback/threading deadlock or reentrancy issue: the handler waits for an async WinRT operation whose completion is not delivered until the handler unwinds or the relevant dispatcher/thread is allowed to progress.

Observed trace pattern:

FrameArrived_Invoke enter
after_TryGetNextFrame
after_CreateCopyFromSurface
WaitForSingleObject timeout=1000 -> WAIT_TIMEOUT
FrameArrived_Invoke leave
SetEvent for the async operation shortly after callback unwind

The local JS fallback avoids this WGC path for screenshots by asking the native helper for the window/accessibility state without a screenshot, then adding a GDI screenshot.

Input path

Native input initially failed before sending events because GetCursorPos returned access denied. This appears to be tied to cursor overlay/state setup rather than the actual ability to send input. Replacing GetCursorPos with a safe success value allowed the input pipeline to proceed.

After that, input failed on impossible window bounds. A Frida hook at the bounds preparation site showed that rewriting the state from GetWindowRect(hwnd) produced stable dimensions and let input pass validation.

Finally, at 125 percent display scaling, the GDI screenshot size was 1152x610, while native input expected logical coordinates. Dividing screenshot coordinates by GetDpiForWindow(hwnd) / 96 made high-level click/drag align with the actual Paint UI.

Local JS client patch

File patched:

%USERPROFILE%\.codex\plugins\cache\openai-bundled\computer-use\26.611.61753\scripts\computer-use-client.mjs

1. GDI screenshot fallback

The patched NativePipeComputerUseClient.get_window_state does this on Windows when screenshots are requested:

  1. Calls the native helper with include_screenshot: false and include_text: true.
  2. Preserves accessibility text only if the caller requested it.
  3. Adds a screenshot captured by a fallback function:
  • GetWindowRect(hwnd) for window bounds.
  • PrintWindow(hwnd, hdc, 2) first.
  • If PrintWindow fails, use GetWindowDC(hwnd) plus BitBlt.
  • Return a data URL with id: "gdi-window", originX, originY, width, height, and zIndex.

Source-level recommendation:

  • Move this fallback into the native Windows helper instead of spawning PowerShell from the JS client.
  • Try WGC first for occlusion-resistant capture.
  • If WGC setup or frame copy fails, fall back to PrintWindow(PW_RENDERFULLCONTENT) and then to GetWindowDC/BitBlt.
  • Mark the screenshot coordinate space explicitly.

2. Native input routing and DPI conversion

The patched proxy routes high-level click and drag through the existing native pipe:

if (
  platform() === "win32" &&
  DIRECT_NATIVE_INPUT_METHODS.has(property) &&
  typeof target.transport?.request === "function"
) {
  return target.transport.request(
    property,
    await scaleWindowInputForGdiScreenshot(args[0] ?? {}, property),
  );
}

The conversion function queries DPI using GetDpiForWindow(hwnd) and divides screenshot coordinates by dpi / 96 before passing them to native input:

scaled[key] = Math.round(value / scale);

Source-level recommendation:

  • Ensure screenshot coordinates and input coordinates use the same coordinate space.
  • If a fallback capture returns physical pixels, either:
  • convert input coordinates to logical pixels, or
  • expose screenshots in logical pixels, or
  • include coordinateSpace and dpiScale metadata and let the client convert.
  • Add automated coverage for 100, 125, 150, and 200 percent DPI.

Runtime hooks used for diagnosis/workaround

GetCursorPos hook

This was enough to bypass GetCursorPos failed: Access is denied and continue with input.

const user32 = Process.getModuleByName("user32.dll");
const getCursorPos = user32.getExportByName("GetCursorPos");

Interceptor.replace(
  getCursorPos,
  new NativeCallback((point) => {
    if (!point.isNull()) {
      point.writeS32(0);
      point.add(4).writeS32(0);
    }
    return 1;
  }, "bool", ["pointer"]),
);

Source-level recommendation:

  • Do not make GetCursorPos success mandatory for input.
  • If cursor overlay initialization cannot read or suppress the system cursor, degrade gracefully:
  • skip overlay animation,
  • use the intended target coordinate as the initial cursor position,
  • still send the requested input if target activation and bounds validation pass.

Bounds repair hook

The hook ran at image RVA 0xAB4A3 and rewrote the native state from GetWindowRect(hwnd). The observed state layout in this build:

  • state + 0xF8: HWND
  • state + 0x198: validity flag
  • state + 0x19C: origin X
  • state + 0x1A0: origin Y
  • state + 0x1A4: width
  • state + 0x1A8: height
  • state + 0x1AC: extra/unknown field set to zero
const image = Process.getModuleByName("codex-computer-use.exe");
const checkBounds = image.base.add(ptr("0xab4a3"));
const GetWindowRect = new NativeFunction(
  Process.getModuleByName("user32.dll").getExportByName("GetWindowRect"),
  "bool",
  ["pointer", "pointer"],
);

Interceptor.attach(checkBounds, {
  onEnter(args) {
    const state = args[1];
    const hwnd = state.add(0xf8).readPointer();
    const rect = Memory.alloc(16);
    if (!GetWindowRect(hwnd, rect)) return;

    const left = rect.readS32();
    const top = rect.add(4).readS32();
    const right = rect.add(8).readS32();
    const bottom = rect.add(12).readS32();
    const width = Math.max(1, right - left);
    const height = Math.max(1, bottom - top);

    state.add(0x198).writeS32(1);
    state.add(0x19c).writeS32(0);
    state.add(0x1a0).writeS32(0);
    state.add(0x1a4).writeS32(width);
    state.add(0x1a8).writeS32(height);
    state.add(0x1ac).writeS32(0);
  },
});

Source-level recommendation:

  • Recompute target bounds immediately before input validation using reliable APIs, for example:
  • DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS) where appropriate.
  • GetWindowRect(hwnd) as a fallback.
  • GetClientRect plus ClientToScreen if the input API expects client coordinates.
  • Validate dimensions with sane limits and reject only if the recomputed bounds are actually invalid.
  • Avoid carrying stale capture-time bounds into later input operations if foreground/window geometry may have changed.

Binary patches observed in the local helper

These offsets are for the tested build layout only. They are useful to correlate native code sites but should not be shipped as a binary patch.

| Purpose | File offset | Local patched bytes | Notes |
| --- | ---: | --- | --- |
| Bypass fatal SetIsBorderRequired branch | 0xBB651 | 90 90 48 8B 8D E0 01 00 00 48 8B 01 FF 50 30 89 | Original branch was observed as 75 3E; patched to NOP/NOP. |
| Async wait timeout argument restored after experiments | 0x124949 | BA FF FF FF FF FF 15 94 8E 00 00 48 89 F1 48 83 | This is not the final fix. Blocking forever also deadlocks; the real fix is to avoid synchronous waiting in the frame callback path. |
| Bypass coordinate gate branch | 0xAA8B2 | 90 90 8B 82 AC 01 00 00 89 46 14 0F 10 82 9C 01 | Original branch was observed as 75 1E; patched to NOP/NOP. Runtime bounds repair was still needed. |
| Activation failure bypass | 0xA22FA | E9 D1 FF FF FF 90 90 6A 45 5A 48 81 C4 F8 00 00 | Replaced a failing error path with a jump to continue. Should be replaced by clearer activation fallback/retry logic. |
| RoInitialize call site | 0x7E7F6 | 31 C9 E8 A2 1C 00 00 90 4C 8D 05 DB 71 0B 00 6A | STA/MTA experiment was reverted. Current code initializes with argument zero. |

Recommended upstream fixes

Capture

  1. Treat SetIsBorderRequired(false) as best effort.
  • Log the failure.
  • Continue capture setup and StartCapture.
  • Do not fail the whole screenshot request solely because the border preference failed.
  1. Remove synchronous waits from the FrameArrived copy path.
  • Do not block inside the frame callback waiting for SoftwareBitmap.CreateCopyFromSurfaceAsync.
  • Either return from the callback and complete the copy on an async task, or avoid WinRT async copying in this callback path.
  • A robust alternative is native D3D11 copy to a staging texture, then map/read pixels synchronously outside the callback.
  1. Add a native fallback capture backend.
  • WGC should remain primary because it can capture occluded windows.
  • If WGC setup/copy fails, use PrintWindow(PW_RENDERFULLCONTENT) and then BitBlt.
  • Include capture backend and coordinate space in the returned screenshot metadata.

Input

  1. Make cursor overlay failures non-fatal.
  • If GetCursorPos, cursor suppression, or overlay animation fails, continue with input if the target window is valid.
  • Use requested target coordinates as the initial cursor position for visualization fallback.
  1. Recompute bounds before input.
  • Use current HWND geometry instead of stale/corrupt cached state.
  • Clamp/validate with sane min/max values.
  • Include diagnostic details if bounds are invalid.
  1. Normalize coordinate systems.
  • Define whether API x/y are physical window pixels, logical window pixels, or client pixels.
  • Keep screenshots and input in the same coordinate system.
  • If mixed backends make that impossible, return dpiScale and coordinateSpace with screenshots and centralize conversion.

JS client

  1. Keep using the native pipe for native input methods.
  2. Avoid ad hoc foreground input implementations outside the helper.
  3. If a temporary JS fallback screenshot backend is used, make coordinate conversion explicit and cache DPI per HWND only while the HWND is valid.

Suggested regression tests

  1. get_window_state({ include_screenshot: true }) succeeds on Paint.
  2. get_window_state({ include_screenshot: true, include_text: true }) returns both screenshot and accessibility text.
  3. click selects a Paint color by screenshot coordinates.
  4. drag draws a visible line on the Paint canvas.
  5. The same click/drag coordinates work at 100, 125, 150, and 200 percent DPI.
  6. WGC failure injection falls back to PrintWindow/BitBlt without crashing the request.
  7. GetCursorPos failure injection does not prevent click/drag.
  8. Target window move/resize between screenshot and input is handled by fresh bounds computation or a clear stale-state error.

Validation result

Using the above local recovery, Paint was controlled through Computer Use and produced visible drawings using real click and drag actions. Final verification screenshots were saved as:

  • outputs\pikachu-paint-final.png
  • outputs\pikachu-paint-second.png

This demonstrates that the native pipe, target activation, click, drag, and screenshot verification can work once the capture fatal path, cursor-position dependency, bounds corruption, and DPI mismatch are addressed.

Ready-to-run patch script

A companion PowerShell script is included as:

outputs\apply-cua-windows-workaround.ps1

Typical usage from the directory containing the script:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\apply-cua-windows-workaround.ps1 -AttachRuntimeHooks

The script intentionally does not hard-code any local username. It discovers paths from $env:USERPROFILE and $env:LOCALAPPDATA, backs up modified files, applies the JS fallback/input patch, applies the helper binary byte patches for the tested build, and optionally attaches the runtime Frida hooks needed for the GetCursorPos and bounds issues.

If frida-inject-*-windows-x86_64.exe is not discoverable next to the script, in the current directory, or in Downloads, pass it explicitly:

powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\apply-cua-windows-workaround.ps1 -AttachRuntimeHooks -FridaInject C:\path\to\frida-inject-17.14.0-windows-x86_64.exe

Safety switches:

# Patch only JS.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\apply-cua-windows-workaround.ps1 -SkipBinaryPatch

# Patch only the helper binary.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\apply-cua-windows-workaround.ps1 -SkipJsPatch

# Allow byte patches on a helper whose size/current bytes differ from the tested build.
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\apply-cua-windows-workaround.ps1 -ForceUnknownBytes

Powerhell Script (Save to your disk and name it apply-cua-windows-workaround.ps1)

[CmdletBinding()]
param(
  [string]$ComputerUsePluginRoot,
  [string]$HelperExe,
  [string]$FridaInject,
  [switch]$AttachRuntimeHooks,
  [switch]$SkipBinaryPatch,
  [switch]$SkipJsPatch,
  [switch]$ForceUnknownBytes
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Write-Step($Message) {
  Write-Host "[cua-patch] $Message"
}

function Resolve-LatestComputerUsePluginRoot {
  if ($ComputerUsePluginRoot) {
    return (Resolve-Path -LiteralPath $ComputerUsePluginRoot).Path
  }

  $root = Join-Path $env:USERPROFILE ".codex\plugins\cache\openai-bundled\computer-use"
  if (-not (Test-Path -LiteralPath $root)) {
    throw "Computer Use plugin cache not found: $root"
  }

  $candidate = Get-ChildItem -LiteralPath $root -Directory |
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1
  if (-not $candidate) {
    throw "No Computer Use plugin versions found under: $root"
  }
  return $candidate.FullName
}

function Resolve-ComputerUseHelper {
  if ($HelperExe) {
    return (Resolve-Path -LiteralPath $HelperExe).Path
  }

  $runtimeRoot = Join-Path $env:LOCALAPPDATA "OpenAI\Codex\runtimes\cua_node"
  if (-not (Test-Path -LiteralPath $runtimeRoot)) {
    throw "Codex CUA runtime root not found: $runtimeRoot"
  }

  $candidate = Get-ChildItem -LiteralPath $runtimeRoot -Recurse -Filter "codex-computer-use.exe" |
    Where-Object { $_.FullName -like "*\node_modules\@oai\sky\bin\windows\codex-computer-use.exe" } |
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1
  if (-not $candidate) {
    throw "codex-computer-use.exe not found under: $runtimeRoot"
  }
  return $candidate.FullName
}

function Backup-File($Path) {
  $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
  $backup = "$Path.bak-$timestamp"
  Copy-Item -LiteralPath $Path -Destination $backup -Force
  Write-Step "backup: $backup"
}

function HexToBytes([string]$Hex) {
  $clean = ($Hex -replace "[^0-9A-Fa-f]", "")
  if (($clean.Length % 2) -ne 0) {
    throw "Invalid hex string: $Hex"
  }
  $bytes = New-Object byte[] ($clean.Length / 2)
  for ($i = 0; $i -lt $bytes.Length; $i++) {
    $bytes[$i] = [Convert]::ToByte($clean.Substring($i * 2, 2), 16)
  }
  return $bytes
}

function BytesEqual([byte[]]$A, [byte[]]$B) {
  if ($A.Length -ne $B.Length) { return $false }
  for ($i = 0; $i -lt $A.Length; $i++) {
    if ($A[$i] -ne $B[$i]) { return $false }
  }
  return $true
}

function Write-BinaryPatch($Path, [int64]$Offset, [byte[]]$Patch, [byte[][]]$Expected, [string]$Name) {
  $fs = [System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::Read)
  try {
    if ($fs.Length -lt ($Offset + $Patch.Length)) {
      throw "$Name offset is outside helper binary: 0x$($Offset.ToString('X'))"
    }

    $current = New-Object byte[] $Patch.Length
    $fs.Seek($Offset, [System.IO.SeekOrigin]::Begin) | Out-Null
    [void]$fs.Read($current, 0, $current.Length)

    if (BytesEqual $current $Patch) {
      Write-Step "$Name already patched"
      return
    }

    $matched = $false
    foreach ($candidate in $Expected) {
      if (BytesEqual $current $candidate) {
        $matched = $true
        break
      }
    }
    if (-not $matched -and -not $ForceUnknownBytes) {
      $got = ($current | ForEach-Object { $_.ToString("X2") }) -join " "
      throw "$Name bytes did not match an expected pattern at 0x$($Offset.ToString('X')). Current: $got. Re-run with -ForceUnknownBytes only if this is the expected helper build."
    }

    $fs.Seek($Offset, [System.IO.SeekOrigin]::Begin) | Out-Null
    $fs.Write($Patch, 0, $Patch.Length)
    Write-Step "$Name patched at 0x$($Offset.ToString('X'))"
  } finally {
    $fs.Dispose()
  }
}

function Patch-HelperBinary($Path) {
  Write-Step "patching helper binary: $Path"
  Backup-File $Path

  $length = (Get-Item -LiteralPath $Path).Length
  if ($length -ne 1691648 -and -not $ForceUnknownBytes) {
    throw "Unexpected helper size: $length. Expected 1691648 for this workaround. Re-run with -ForceUnknownBytes to override."
  }

  Write-BinaryPatch $Path 0xBB651 (HexToBytes "90 90") @(
    (HexToBytes "75 3E"),
    (HexToBytes "90 90")
  ) "SetIsBorderRequired fatal branch bypass"

  Write-BinaryPatch $Path 0xAA8B2 (HexToBytes "90 90") @(
    (HexToBytes "75 1E"),
    (HexToBytes "90 90")
  ) "coordinate gate branch bypass"

  Write-BinaryPatch $Path 0xA22FA (HexToBytes "E9 D1 FF FF FF 90 90") @(
    (HexToBytes "48 8D 0D 9F EE 08 00"),
    (HexToBytes "E9 D1 FF FF FF 90 90")
  ) "activation failure bypass"

  Write-BinaryPatch $Path 0x124949 (HexToBytes "BA FF FF FF FF") @(
    (HexToBytes "BA FF FF FF FF"),
    (HexToBytes "BA E8 03 00 00")
  ) "copy-surface wait argument restore"

  Write-BinaryPatch $Path 0x7E7F6 (HexToBytes "31 C9 E8 A2 1C 00 00 90") @(
    (HexToBytes "31 C9 E8 A2 1C 00 00 90")
  ) "RoInitialize call-site sanity check"
}

function Patch-JsClient($ClientPath) {
  Write-Step "patching JS client: $ClientPath"
  Backup-File $ClientPath

  $text = Get-Content -LiteralPath $ClientPath -Raw

  if ($text -notmatch "DIRECT_NATIVE_INPUT_METHODS") {
    $needle = 'const TOOL_SURFACE_META_KEY = "codex/toolSurface";'
    $insert = @'
const DIRECT_NATIVE_INPUT_METHODS = new Set([
  "click",
  "drag",
]);
'@
    $text = $text.Replace($needle, "$needle`r`n$insert")
  }

  if ($text -notmatch "windowDpiScaleCache") {
    $text = $text.Replace("let activeSkyClient = null;", "let activeSkyClient = null;`r`nconst windowDpiScaleCache = new Map();")
  }

  if ($text -notmatch "async get_window_state\(input\)") {
    $method = @'
  async get_window_state(input) {
    const wantsScreenshot = input?.include_screenshot !== false;
    if (!wantsScreenshot || platform() !== "win32") {
      return super.get_window_state(input);
    }

    const state = await super.get_window_state({
      ...input,
      include_screenshot: false,
      include_text: true,
    });
    return {
      ...state,
      accessibility: input?.include_text === true ? state.accessibility : null,
      screenshots: [await captureWindowScreenshot(state.window ?? input.window)],
    };
  }

'@
    $text = $text -replace "class NativePipeComputerUseClient extends WindowsComputerUseClientBase \{\r?\n", "class NativePipeComputerUseClient extends WindowsComputerUseClientBase {`r`n$method"
  }

  if ($text -notmatch "async function captureWindowScreenshot") {
    $helpers = @'
async function captureWindowScreenshot(window) {
  const hwnd = Number(window?.id);
  if (!Number.isInteger(hwnd) || hwnd <= 0) {
    throw new Error(`Cannot capture screenshot for invalid window id: ${window?.id}`);
  }

  const script = `
$ErrorActionPreference = 'Stop'
$Hwnd = ${hwnd}
Add-Type -AssemblyName System.Drawing
$typeDefinition = 'using System; using System.Runtime.InteropServices; public static class CodexGdiWindowCapture { [DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); [DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags); [DllImport("user32.dll")] public static extern IntPtr GetWindowDC(IntPtr hWnd); [DllImport("user32.dll")] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll")] public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSrc, int xSrc, int ySrc, int rop); public struct RECT { public int Left; public int Top; public int Right; public int Bottom; } }'
Add-Type -TypeDefinition $typeDefinition
$rect = New-Object CodexGdiWindowCapture+RECT
$hwndPtr = [IntPtr]$Hwnd
if (-not [CodexGdiWindowCapture]::GetWindowRect($hwndPtr, [ref]$rect)) {
  throw "GetWindowRect failed"
}
$width = [Math]::Max(1, $rect.Right - $rect.Left)
$height = [Math]::Max(1, $rect.Bottom - $rect.Top)
$bitmap = New-Object System.Drawing.Bitmap $width, $height, ([System.Drawing.Imaging.PixelFormat]::Format32bppArgb)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
try {
  $hdc = $graphics.GetHdc()
  try {
    $printed = [CodexGdiWindowCapture]::PrintWindow($hwndPtr, $hdc, 2)
  } finally {
    $graphics.ReleaseHdc($hdc)
  }
  if (-not $printed) {
    $src = [CodexGdiWindowCapture]::GetWindowDC($hwndPtr)
    if ($src -eq [IntPtr]::Zero) { throw "GetWindowDC failed" }
    $dstGraphics = [System.Drawing.Graphics]::FromImage($bitmap)
    try {
      $dst = $dstGraphics.GetHdc()
      try {
        [void][CodexGdiWindowCapture]::BitBlt($dst, 0, 0, $width, $height, $src, 0, 0, 0x00CC0020)
      } finally {
        $dstGraphics.ReleaseHdc($dst)
      }
    } finally {
      $dstGraphics.Dispose()
      [void][CodexGdiWindowCapture]::ReleaseDC($hwndPtr, $src)
    }
  }
  $stream = New-Object System.IO.MemoryStream
  try {
    $bitmap.Save($stream, [System.Drawing.Imaging.ImageFormat]::Png)
    $base64 = [Convert]::ToBase64String($stream.ToArray())
  } finally {
    $stream.Dispose()
  }
} finally {
  $graphics.Dispose()
  $bitmap.Dispose()
}
@{
  id = "gdi-window"
  width = $width
  height = $height
  originX = $rect.Left
  originY = $rect.Top
  zIndex = 0
  url = "data:image/png;base64,$base64"
} | ConvertTo-Json -Compress
`;

  const stdout = await runPowerShell(script, 10000);
  return JSON.parse(stdout);
}

function runPowerShell(script, timeoutMs) {
  return new Promise((resolve, reject) => {
    const encodedScript = Buffer.from(script, "utf16le").toString("base64");
    const child = spawn(
      "powershell.exe",
      [
        "-NoProfile",
        "-NonInteractive",
        "-ExecutionPolicy",
        "Bypass",
        "-EncodedCommand",
        encodedScript,
      ],
      { stdio: ["ignore", "pipe", "pipe"], windowsHide: true },
    );
    let stdout = "";
    let stderr = "";
    const timer = setTimeout(() => {
      child.kill();
      reject(new Error("PowerShell helper timed out"));
    }, timeoutMs);

    child.stdout.setEncoding("utf8");
    child.stderr.setEncoding("utf8");
    child.stdout.on("data", (chunk) => {
      stdout += chunk;
    });
    child.stderr.on("data", (chunk) => {
      stderr += chunk;
    });
    child.on("error", (error) => {
      clearTimeout(timer);
      reject(error);
    });
    child.on("close", (code) => {
      clearTimeout(timer);
      if (code === 0) {
        resolve(stdout.trim());
      } else {
        reject(new Error(stderr.trim() || `PowerShell exited with code ${code}`));
      }
    });
  });
}

async function scaleWindowInputForGdiScreenshot(input, method) {
  const scale = await getWindowDpiScale(input?.window);
  if (!Number.isFinite(scale) || scale <= 0 || Math.abs(scale - 1) < 0.001) {
    return input;
  }

  const scaled = { ...input };
  const coordinateKeys =
    method === "click"
      ? ["x", "y"]
      : method === "drag"
        ? ["from_x", "from_y", "to_x", "to_y"]
        : [];

  for (const key of coordinateKeys) {
    const value = Number(input?.[key]);
    if (Number.isFinite(value)) {
      scaled[key] = Math.round(value / scale);
    }
  }

  return scaled;
}

async function getWindowDpiScale(window) {
  const hwnd = Number(window?.id);
  if (!Number.isInteger(hwnd) || hwnd <= 0) {
    return 1;
  }

  const cached = windowDpiScaleCache.get(hwnd);
  if (cached != null) {
    return cached;
  }

  const script = `
$ErrorActionPreference = 'Stop'
$Hwnd = [IntPtr]${hwnd}
$typeDefinition = 'using System; using System.Runtime.InteropServices; public static class CodexDpi { [DllImport("user32.dll")] public static extern uint GetDpiForWindow(IntPtr hWnd); }'
Add-Type -TypeDefinition $typeDefinition
$dpi = [CodexDpi]::GetDpiForWindow($Hwnd)
if ($dpi -le 0) { $dpi = 96 }
([double]$dpi / 96.0).ToString([System.Globalization.CultureInfo]::InvariantCulture)
`;

  let scale = 1;
  try {
    const output = await runPowerShell(script, 5000);
    const parsed = Number(output);
    if (Number.isFinite(parsed) && parsed > 0) {
      scale = parsed;
    }
  } catch {
    scale = 1;
  }

  windowDpiScaleCache.set(hwnd, scale);
  return scale;
}

'@
    $transportClassPattern = [regex]"\r?\nclass NativePipeComputerUseTransport \{"
    $text = $transportClassPattern.Replace(
      $text,
      { param($match) "`r`n$helpers`r`nclass NativePipeComputerUseTransport {" },
      1
    )
  }

  if ($text -notmatch "scaleWindowInputForGdiScreenshot\(args\[0\]") {
    $needle = '        return Reflect.apply(value, target, args);'
    $intercept = @'
        if (
          platform() === "win32" &&
          DIRECT_NATIVE_INPUT_METHODS.has(property) &&
          typeof target.transport?.request === "function"
        ) {
          return target.transport.request(
            property,
            await scaleWindowInputForGdiScreenshot(args[0] ?? {}, property),
          );
        }
        if (
          property === "get_window_state" &&
          platform() === "win32" &&
          args[0]?.include_screenshot !== false
        ) {
          const input = args[0];
          const state = await Reflect.apply(value, target, [
            {
              ...input,
              include_screenshot: false,
              include_text: true,
            },
          ]);
          return {
            ...state,
            accessibility: input?.include_text === true ? state.accessibility : null,
            screenshots: [
              await captureWindowScreenshot(state.window ?? input.window),
            ],
          };
        }
        return Reflect.apply(value, target, args);
'@
    $index = $text.IndexOf($needle)
    if ($index -lt 0) {
      throw "Could not find installComputerUseRuntime return site in JS client."
    }
    $text = $text.Remove($index, $needle.Length).Insert($index, $intercept)
  }

  Set-Content -LiteralPath $ClientPath -Value $text -Encoding UTF8 -NoNewline
  Write-Step "JS client patched"
}

function Resolve-FridaInject {
  if ($FridaInject) {
    return (Resolve-Path -LiteralPath $FridaInject).Path
  }

  $roots = @(
    $PSScriptRoot,
    (Get-Location).Path,
    (Join-Path $env:USERPROFILE "Downloads")
  ) | Where-Object { $_ -and (Test-Path -LiteralPath $_) } | Select-Object -Unique

  foreach ($root in $roots) {
    $candidate = Get-ChildItem -LiteralPath $root -Recurse -Filter "frida-inject-*-windows-x86_64.exe" -ErrorAction SilentlyContinue |
      Sort-Object LastWriteTime -Descending |
      Select-Object -First 1
    if ($candidate) {
      return $candidate.FullName
    }
  }

  return $null
}

function Write-RuntimeHookScripts($Directory) {
  New-Item -ItemType Directory -Path $Directory -Force | Out-Null

  $cursorHook = @'
const user32 = Process.getModuleByName("user32.dll");
const getCursorPos = user32.getExportByName("GetCursorPos");
Interceptor.replace(
  getCursorPos,
  new NativeCallback((point) => {
    if (!point.isNull()) {
      point.writeS32(0);
      point.add(4).writeS32(0);
    }
    return 1;
  }, "bool", ["pointer"]),
);
console.log("[cua-patch] GetCursorPos hook installed");
'@

  $boundsHook = @'
const image = Process.getModuleByName("codex-computer-use.exe");
const checkBounds = image.base.add(ptr("0xab4a3"));
const user32 = Process.getModuleByName("user32.dll");
const GetWindowRect = new NativeFunction(
  user32.getExportByName("GetWindowRect"),
  "bool",
  ["pointer", "pointer"],
);

Interceptor.attach(checkBounds, {
  onEnter(args) {
    const state = args[1];
    const hwnd = state.add(0xf8).readPointer();
    const rect = Memory.alloc(16);
    if (!GetWindowRect(hwnd, rect)) return;

    const left = rect.readS32();
    const top = rect.add(4).readS32();
    const right = rect.add(8).readS32();
    const bottom = rect.add(12).readS32();
    const width = Math.max(1, right - left);
    const height = Math.max(1, bottom - top);

    state.add(0x198).writeS32(1);
    state.add(0x19c).writeS32(0);
    state.add(0x1a0).writeS32(0);
    state.add(0x1a4).writeS32(width);
    state.add(0x1a8).writeS32(height);
    state.add(0x1ac).writeS32(0);
  },
});
console.log("[cua-patch] bounds hook installed");
'@

  $cursorPath = Join-Path $Directory "cua-hook-getcursorpos.js"
  $boundsPath = Join-Path $Directory "cua-hook-bounds.js"
  Set-Content -LiteralPath $cursorPath -Value $cursorHook -Encoding UTF8
  Set-Content -LiteralPath $boundsPath -Value $boundsHook -Encoding UTF8
  return @($cursorPath, $boundsPath)
}

function Attach-RuntimeHooks {
  $inject = Resolve-FridaInject
  if (-not $inject) {
    throw "frida-inject was not found. Download frida-inject-*-windows-x86_64.exe or pass -FridaInject <path>."
  }

  $proc = Get-Process -Name "codex-computer-use" -ErrorAction SilentlyContinue |
    Sort-Object StartTime -Descending |
    Select-Object -First 1
  if (-not $proc) {
    Write-Step "codex-computer-use.exe is not running; start Computer Use and rerun with -AttachRuntimeHooks."
    return
  }

  $hookDir = Join-Path $PSScriptRoot "cua-runtime-hooks"
  $hooks = Write-RuntimeHookScripts $hookDir
  foreach ($hook in $hooks) {
    $name = [System.IO.Path]::GetFileNameWithoutExtension($hook)
    $stdout = Join-Path $hookDir "$name.out.log"
    $stderr = Join-Path $hookDir "$name.err.log"
    Start-Process -FilePath $inject -ArgumentList @("-p", [string]$proc.Id, "-s", $hook) -WindowStyle Hidden -RedirectStandardOutput $stdout -RedirectStandardError $stderr
    Write-Step "attached runtime hook: $hook"
  }
}

$pluginRoot = Resolve-LatestComputerUsePluginRoot
$clientPath = Join-Path $pluginRoot "scripts\computer-use-client.mjs"
if (-not (Test-Path -LiteralPath $clientPath)) {
  throw "computer-use-client.mjs not found: $clientPath"
}

if (-not $SkipJsPatch) {
  Patch-JsClient $clientPath
}

if (-not $SkipBinaryPatch) {
  $helperPath = Resolve-ComputerUseHelper
  Patch-HelperBinary $helperPath
}

if ($AttachRuntimeHooks) {
  Attach-RuntimeHooks
} else {
  Write-Step "runtime hooks not attached. To attach them, rerun with -AttachRuntimeHooks and make sure frida-inject is available."
}

Write-Step "done"

Success Pic

<img width="1152" height="610" alt="Image" src="https://github.com/user-attachments/assets/233ecd99-6d74-4464-bc64-82c0b4a65d99" />

jerryfang527 · 1 month ago

Update: Still broken after upgrading to 1.2.14

Upgraded from 1.2.9 to 1.2.14, but this bug is still not fixed.

Status Summary

| Item | Status |
|------|--------|
| Version upgraded | ? 1.2.9 ? 1.2.14 |
| Screenshot still broken | ? Same SetIsBorderRequired error |
| Text / accessibility tree | ? Working |
| Window operations (activate, list) | ? Working |

Error (unchanged)

SetIsBorderRequired failed: ?????? (0x80004002)

Environment

  • Codex Desktop version: 1.2.14
  • OS: Windows 10 Pro 22H2 (build 19045)
  • Same reproduction as described in the original issue

This confirms the fix has not been shipped in 1.2.14. The SetIsBorderRequired call still fails on Windows 10 build 19045.

Please prioritize this - Windows Computer Use is partially unusable without screenshots.

darlingm contributor · 11 days ago

The rebranded ChatGPT Desktop App (26.707.30751, released Jul 9, 2026) has the same problem. I don't want to post a bunch of "still not fixed" messages, but figured with the new app that it was useful to confirm.

zengfanfan · 10 days ago

anyone fix this ???

bar-sharabani · 8 days ago

Still happening on Windows 10. Asked GPT-5.6 Sol Medium, with Computer Use enabled, to use the open Chrome window on my YouTube Playlists tab to extract the names of the songs from all playlists. Screenshot attached.

<img width="744" height="813" alt="Image" src="https://github.com/user-attachments/assets/f12b98a3-566c-4b46-9214-f49bc1926d54" />

Teslajia · 5 days ago

need fixed this bug too!

Pluglug · 5 days ago

Still reproducible on a newer 26.707 build.

Environment:

  • Windows 10 Home 22H2, build 19045.6466
  • Codex/ChatGPT Desktop release: 26.707.72221
  • Microsoft Store package: OpenAI.Codex_26.707.9981.0_x64
  • bundled Computer Use plugin: 26.707.72221

A fresh projectless diagnostic task tested five new owned specimens:

  • Blender 5.1.1 immediately after launch / isolated empty config, maximized
  • Blender after Quick Setup-equivalent state, splash enabled, maximized
  • Blender stable main window, splash disabled, maximized
  • Blender stable main window, splash disabled, normal window
  • fresh Notepad, normal window

All five screenshot requests failed with the same error:

SetIsBorderRequired failed: インターフェイスがサポートされていません (0x80004002)

Window enumeration, activation, foreground verification, and accessibility-only UI reads worked. The failure is not Blender-specific; it is the Windows screenshot capture path. All owned processes and disposable profiles were cleaned up, with residue 0.

Focused diagnostic session available for private sharing through in-app feedback:

019f65ef-2366-7a51-ba48-b5c492c24115

The parent/coordinator thread is 019f60fc-2f41-7ce3-9f1b-051cbe51eec4, but it contains unrelated PR validation history, so the focused child is the better artifact.