Skip to content

cua-driver: handle macOS 26.4 SCK streaming-start failure - #1478

Merged
ddupont808 merged 2 commits into
mainfrom
worktree-agent-a184c8d1924af2793
May 12, 2026
Merged

cua-driver: handle macOS 26.4 SCK streaming-start failure#1478
ddupont808 merged 2 commits into
mainfrom
worktree-agent-a184c8d1924af2793

Conversation

@f-trycua

@f-trycua f-trycua commented May 12, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1467.

Problem

On macOS 26.4.1 (Build 25E253) on physical Apple Silicon Macs, ScreenCaptureKit intermittently refuses to capture specific windows: SCScreenshotManager.captureImage returns SCStreamError code -3801 ("Could not start streaming because audio/video capture failed", localized in Japanese as "オーディオ/ビデオの取り込みがうまくいかなかったため、ストリーミングを開始できませんでした"). Even screencapture -l<id> fails on the same window — so it's not just an SDK glitch; the SCK daemon itself is refusing that specific window.

cua-driver screenshot and get_window_state (with capture_mode: som / vision) surface this as an opaque error with no actionable next step, even though capture_mode: ax would work fine for element-indexed workflows. Related: openclaw/Peekaboo#121.

Summary

  • WindowCapture.captureWindow retries once after a 250 ms back-off when SCK reports a streaming-start failure (covers genuinely transient -3801s).
  • Falls back to CGWindowListCreateImage when SCK still refuses. The legacy API is deprecated on macOS 15+ but remains functional and frequently succeeds where the new SCK pipeline refuses on 26.4.x. The deprecation warning is silenced via an @available(*, deprecated) shim so the call-site stays clean.
  • New CaptureError.streamingFailed distinguishes the SCK regression from generic capture failures, so the tool layer can render an actionable hint without string-matching.
  • get_window_state no longer hard-fails on streaming errors: it ships the AX tree with a warning line nudging the caller toward capture_mode: ax. Element-indexed clicks keep working even when no screenshot is available.
  • screenshot returns a clear error response naming the regression and listing workarounds (different window_id, switch to capture_mode: ax, retry).
  • Permission detection now covers Japanese ("許可", "拒否") in addition to English phrasings.
  • Docs: new FAQ entry on the SCK regression; auto-generated mcp-tools.mdx regenerated from the updated tool descriptions.

Files changed

  • libs/cua-driver/Sources/CuaDriverCore/Capture/WindowCapture.swift — retry, CGWindowList fallback, error classification.
  • libs/cua-driver/Sources/CuaDriverServer/Tools/ScreenshotTool.swift — actionable error response.
  • libs/cua-driver/Sources/CuaDriverServer/Tools/GetWindowStateTool.swift — non-fatal streaming failure with hint.
  • docs/content/docs/cua-driver/guide/getting-started/faq.mdx — new FAQ entry.
  • docs/content/docs/cua-driver/reference/mcp-tools.mdx / cli-reference.mdx — regenerated.

Test plan

  • swift build clean (no new warnings)
  • swift test — all 26 tests pass
  • bun run scripts/docs-generators/cua-driver.ts --check — docs in sync
  • Reproduce on a macOS 26.4 physical Mac (could not verify locally — author is on a different version). The retry + CGWindowList fallback paths are unit-test-friendly but require the failing hardware for end-to-end validation. Filing as a code-path fix; once a reporter on 26.4 confirms the new error message, we can iterate further if needed.
  • Manual: cua-driver screenshot '{"window_id": <known-good-id>}' still works on healthy windows (i.e. the fallback path doesn't regress the happy case).
  • Manual: cua-driver get_window_state '{"pid":<pid>,"window_id":<id>}' returns AX tree + hint when capture fails.

Notes

  • CaptureError.streamingFailed does not propagate through RecordingSession — recording still swallows any throw and emits shot = nil, so the change is additive there.
  • The CGWindowList fallback uses .boundsIgnoreFraming + .bestResolution and rejects 1×1 placeholder images (the legacy API returns those for occluded windows; they're worse than no image).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved screenshot capture handling on macOS: retries on failures, falls back to legacy method, provides actionable workarounds.
    • get_window_state now gracefully handles screenshot failures, returning the AX tree with guidance.
  • Documentation

    • Added FAQ covering screenshot and window state failures with recovery options.
    • Version bumped to 0.1.6.
    • Enhanced MCP tools reference with updated behavior descriptions and platform-specific guidance.

Review Change Stack

…1467)

ScreenCaptureKit on macOS 26.4.x regresses for specific windows on
physical Macs — `SCScreenshotManager.captureImage` returns "Could not
start streaming because audio/video capture failed" (SCStreamError
-3801, sometimes localized in Japanese / other locales) and even
`screencapture -l<id>` refuses the same window. Without a hint,
`get_window_state` and `screenshot` callers had no actionable path
forward.

Changes:
- `WindowCapture.captureWindow` now retries the SCK call once after a
  brief delay (covers transient -3801) and falls back to
  `CGWindowListCreateImage` (deprecated but still functional, works on
  many windows where SCK refuses).
- New `CaptureError.streamingFailed` distinguishes the regression from
  generic capture failures so the tool layer can render an actionable
  hint instead of an opaque message.
- `get_window_state` no longer hard-fails on streaming errors: AX tree
  still ships in the response with a warning line pointing at
  `capture_mode: ax`. Element-indexed clicks keep working.
- `screenshot` returns a clear error response naming the regression and
  suggesting workarounds (different `window_id`, `capture_mode: ax`,
  retry).
- Error classification now detects Japanese permission strings ("許可",
  "拒否") in addition to English.

Docs:
- FAQ entry covering the macOS 26.4 SCK regression and workarounds.
- mcp-tools.mdx regenerated (tool descriptions updated).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview May 12, 2026 1:10pm

Request Review

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c455638-1821-49a4-b5a8-78e72b748922

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds resilience for a macOS 26.4.x ScreenCaptureKit streaming regression where screenshot capture is refused for certain windows. It implements retry logic with legacy fallback in WindowCapture, non-fatal error handling in get_window_state, specialized error messages in screenshot, and comprehensive documentation with version bump to 0.1.6.

Changes

ScreenCaptureKit Streaming Failure Resilience

Layer / File(s) Summary
WindowCapture error classification and retry/fallback logic
libs/cua-driver/Sources/CuaDriverCore/Capture/WindowCapture.swift
New CaptureError.streamingFailed(String) case distinguishes streaming-start failures from generic capture errors. captureWindow now attempts capture with single retry on streaming failures, falls back to legacy CGWindowListCreateImage for non-permission errors. Helpers captureSCKWithRetry, legacyCaptureWindow, and legacyCGWindowImage support the flow. Error classification detects SCStreamErrorDomain code −3801 and "could not start streaming" / "streaming" message patterns.
get_window_state non-fatal capture handling
libs/cua-driver/Sources/CuaDriverServer/Tools/GetWindowStateTool.swift
Tool documentation clarifies that screenshot capture failures in som/vision modes are non-fatal. Implementation adds captureHint variable and dedicated catch branch for CaptureError.streamingFailed that swallows the failure, preserves the AX tree, and appends an actionable hint recommending capture_mode: ax for subsequent turns.
screenshot tool specialized streaming error response
libs/cua-driver/Sources/CuaDriverServer/Tools/ScreenshotTool.swift
Adds dedicated catch CaptureError.streamingFailed branch returning a specialized error message with concrete workarounds: alternate window_id, capture_mode ax with get_window_state, or retrying later.
Documentation and version updates
docs/content/docs/cua-driver/reference/cli-reference.mdx, docs/content/docs/cua-driver/reference/mcp-tools.mdx, docs/content/docs/cua-driver/guide/getting-started/faq.mdx
Version bumped from 0.1.1 to 0.1.6. get_window_state docs explain non-fatal capture failures. hotkey and press_key clarified for NSMenu key equivalents via FocusWithoutRaise. launch_app describes background launch behavior and Finder URL handling. screenshot docs detail macOS 26.4.x ScreenCaptureKit regression, retry-then-fallback behavior, and workarounds. New FAQ section covers failure modes, driver handling, and prioritized mitigation steps.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • trycua/cua#1386: Modifies get_window_state tool with related exception handling and response accumulation logic.
  • trycua/cua#1424: Updates screenshot tool and related documentation for consistency.

Poem

A Mac captured its screen with a frown,
But ScreenCaptureKit let it down.
We retry and fall back with grace,
Show hints and find workarounds in place—
Now even refused windows wear the crown. 🐰📸

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main technical change: handling a ScreenCaptureKit (SCK) streaming-start failure on macOS 26.4.
Linked Issues check ✅ Passed The PR fully addresses #1467 objectives: distinguishes SCK failures with new error type, retries and falls back to legacy API, allows AX-only capture mode, surfaces actionable guidance, and documents the regression.
Out of Scope Changes check ✅ Passed All changes are focused on handling SCK streaming failures and fall within the objectives of #1467; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-agent-a184c8d1924af2793

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/cua-driver/Sources/CuaDriverServer/Tools/GetWindowStateTool.swift`:
- Around line 224-227: The code currently swallows `.streamingFailed` into a
non-fatal path by setting `captureHint` to a hint string; change the error
handling in GetWindowStateTool (the branches around the `captureHint` usage) so
that when the error is `.streamingFailed` and the tool is running in `vision`
mode you do not treat it as non‑fatal—propagate or return the failure (i.e.,
produce an error result) instead of returning a "successful" result with an AX
hint; retain the non‑fatal behavior only for `som` mode. Apply the same change
to the other similar blocks mentioned (the handling around lines 254–268 and
275–277) so `.streamingFailed` is fatal for `vision` but preserved as hint-only
for `som`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc5a8253-61ed-4bc2-9a75-3926a5a06c20

📥 Commits

Reviewing files that changed from the base of the PR and between 31bc4f8 and abeae0d.

📒 Files selected for processing (6)
  • docs/content/docs/cua-driver/guide/getting-started/faq.mdx
  • docs/content/docs/cua-driver/reference/cli-reference.mdx
  • docs/content/docs/cua-driver/reference/mcp-tools.mdx
  • libs/cua-driver/Sources/CuaDriverCore/Capture/WindowCapture.swift
  • libs/cua-driver/Sources/CuaDriverServer/Tools/GetWindowStateTool.swift
  • libs/cua-driver/Sources/CuaDriverServer/Tools/ScreenshotTool.swift

Comment on lines +224 to +227
// A `.streamingFailed` (macOS 26.4 SCK regression) is also
// swallowed: the AX tree is still useful, and the summary
// line gets an actionable hint instead of a hard error.
var captureHint: String? = nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don’t swallow .streamingFailed in vision mode.

This path currently treats streaming failure as non-fatal for both som and vision, but vision has no AX tree to preserve. It can return a “successful” result with no usable payload and a misleading AX hint.

💡 Suggested fix
-                    } catch CaptureError.streamingFailed(let msg) {
+                    } catch CaptureError.streamingFailed(let msg) {
+                        if captureMode == .vision {
+                            throw CaptureError.streamingFailed(msg)
+                        }
                         // Known macOS 26.4 SCK regression: streaming-start
                         // fails for specific windows on physical Macs and
                         // even CGWindowList can't get pixels back. We don't
                         // fail the call — the AX snapshot is still useful

Also applies to: 254-268, 275-277

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/cua-driver/Sources/CuaDriverServer/Tools/GetWindowStateTool.swift`
around lines 224 - 227, The code currently swallows `.streamingFailed` into a
non-fatal path by setting `captureHint` to a hint string; change the error
handling in GetWindowStateTool (the branches around the `captureHint` usage) so
that when the error is `.streamingFailed` and the tool is running in `vision`
mode you do not treat it as non‑fatal—propagate or return the failure (i.e.,
produce an error result) instead of returning a "successful" result with an AX
hint; retain the non‑fatal behavior only for `som` mode. Apply the same change
to the other similar blocks mentioned (the handling around lines 254–268 and
275–277) so `.streamingFailed` is fatal for `vision` but preserved as hint-only
for `som`.

…bbit)

In `vision` capture mode the screenshot IS the deliverable, so swallowing
`CaptureError.streamingFailed` and returning an AX-only response was
misleading — the AX tree is also skipped in that mode, so the caller got
neither a screenshot nor a tree. Surface the same actionable error the
standalone `screenshot` tool already emits (try alternate window, switch
to `capture_mode: ax`, or retry).

`som` mode is unchanged: the AX tree is still useful for element-indexed
clicks, so the streaming-start failure keeps emitting a non-fatal hint
on the summary line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@f-trycua

Copy link
Copy Markdown
Collaborator Author

@coderabbitai Addressed in a8a92b5. In vision mode, CaptureError.streamingFailed now returns isError: true with the same actionable hint as ScreenshotTool (try a different window_id, switch to capture_mode: ax, or retry). som mode keeps the non-fatal hint behavior — the AX tree alone is still useful there.

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@f-trycua Thanks for the update! The mode-specific approach makes good sense — hard-failing with actionable guidance in vision mode (where a screenshot is essential) while keeping the non-fatal fallback in som mode (where the AX tree alone remains useful) is a well-reasoned distinction.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

@ddupont808
ddupont808 merged commit e8e6291 into main May 12, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cua-driver screenshot fails on physical Mac (macOS 26.4.1): SCStream capture error

3 participants