{"record":{"id":"69111edbdb2ff34a","repo":"Hmbown/CodeWhale","slug":"background-preview-capture-failed-r-stderr","errorCode":null,"errorMessage":"background preview capture failed: ${r.stderr}","messagePattern":"background preview capture failed: (.+?)","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":333,"sourceCode":"    if (tool === \"pointer_sequence\") requireSharedPointer();\n    if (!exec.runInputLease) throw new ExecError(\"This executor cannot safely own held input; update Computer Use\");\n    if ((await native(\"input_capabilities\"))?.input_lease !== 1) throw new ExecError(\"The native helper needs an update for disconnect-safe held input\");\n    const helper = await nativeHelper();\n    try {\n      return await exec.runInputLease(helper, [JSON.stringify({ tool, args: { ...args, ...yieldArgs, input_app_ref: state.inputApp, foreground_input: state.foregroundInput, owner_pipe: true, input_lease: true } })]);\n    } catch (error) {\n      error.code = nativeErrorCode(error.message) ?? error.code;\n      throw error;\n    }\n  }\n\n  async function updatePreview(show = false) {\n    const win = await native(\"window_info\", { app_ref: state.inputApp });\n    const dir = path.join(stateDir(), \"preview\");\n    fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n    const temp = path.join(dir, \"next.png\"), file = path.join(dir, \"latest.png\");\n    const r = await runL(\"screencapture\", [\"-x\", \"-o\", \"-l\", String(win.window_id), \"-t\", \"png\", temp], { timeoutMs: 8000 });\n    if (r.code !== 0) throw new ExecError(`background preview capture failed: ${r.stderr}`);\n    fs.renameSync(temp, file);\n    const p = state.pointer;\n    // The user's own hardware cursor goes on the preview too, so the panel\n    // shows both pointers in the same window-relative space.\n    let userCursor = null;\n    try { userCursor = await native(\"cursor_position\"); } catch {}\n    await native(\"preview_notify\", { enabled: true, show, title: `Codewhale · ${win.name} · ${state.foregroundInput ? \"Shared desktop control\" : \"Background app control\"}`, x: p ? (p.x-win.points.x)/win.points.w : -1, y: p ? (p.y-win.points.y)/win.points.h : -1,\n      user_x: userCursor && Number.isFinite(userCursor.x) ? (userCursor.x-win.points.x)/win.points.w : -1,\n      user_y: userCursor && Number.isFinite(userCursor.y) ? (userCursor.y-win.points.y)/win.points.h : -1 });\n    // Any successful capture (bind, explicit preview, action refresh) starts\n    // the live refresh; the tick itself re-enters this function as a no-op.\n    if (state.previewEnabled && state.inputApp) startPreviewLoop();\n    return { enabled: true, file, app: state.inputApp, pointer: p };\n  }\n\n  // ---------- pointer input ----------\n  // Our qualified raw pointer path uses the shared event tap, which moves\n  // the user's real cursor. Process/window-directed mouse delivery has not","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L315-L351","documentation":"updatePreview captures a window-scoped screenshot of the bound app's window with macOS `screencapture -x -o -l <window_id>`, writing to a temp PNG that is renamed into the preview path. If screencapture exits non-zero, this error surfaces its stderr. Capture is a preview-only step; its failure means no background screenshot was produced.","triggerScenarios":"Calling the background preview/observe flow where `screencapture` fails: invalid or closed window_id from native(\"window_info\"), missing Screen Recording permission for the host process, or a screencapture timeout (8s) / tool error reported via stderr.","commonSituations":"First-run macOS privacy prompt for Screen Recording denied or never granted; target window closed between window_info and the capture; running in an SSH/headless session without GUI access; macOS update resetting TCC permissions for the terminal/app.","solutions":["Grant Screen Recording permission (System Settings → Privacy & Security → Screen Recording) to the app running the backend, then restart it.","Re-run open_application/observe so window_info returns a fresh, live window_id before capturing.","Test the capture manually: `screencapture -x -o -l <window_id> /tmp/t.png` to see the raw error.","If stderr shows a stale window id after app restart, rebind the application (pid/window changed)."],"exampleFix":"// before\nawait updatePreview(); // background preview capture failed: could not create image from window id\n\n// after: rebind to refresh the window id, ensure screen-recording permission\nawait open_application(app.ref);\nawait observe(); // triggers a fresh capture","handlingStrategy":"try-catch","validationCode":"const win = await native('window_info', { app_ref: appRef });\nif (!win?.window_id) throw new Error('no live window to capture — re-observe first');","typeGuard":null,"tryCatchPattern":"try {\n  await updatePreview();\n} catch (e) {\n  if (String(e.message).startsWith('background preview capture failed')) {\n    console.error('Check Screen Recording permission and that the window is still open:', e.message);\n    await open_application(appRef); // refresh window_id, then retry once\n  } else throw e;\n}","preventionTips":["Grant Screen Recording permission to the host app before starting automation and after macOS upgrades.","Refresh window_info before each capture; never reuse window ids across rebinding.","Smoke-test `screencapture -x -o -l <id> /tmp/t.png` in environment preflight.","Treat preview failure as non-fatal where the workflow can continue on accessibility data alone."],"tags":["macos","screencapture","permissions","preview"],"backgroundTag":"command-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}