Hmbown/CodeWhale · error · ExecError
open_application first to choose the preview app
Error message
open_application first to choose the preview app
What it means
Thrown when enabling live preview (preview({enabled:true})) in the macOS backend before any target application has been chosen. The preview loop renders through a dedicated preview app that open_application establishes (stored in state.inputApp); without it there is nothing to attach the preview surface to. The backend requires that ordering rather than silently picking an app.
Solutions
- Call open_application (with the target app name/pid) before enabling preview.
- If preview should be off, call preview({enabled:false}) — that path is allowed and just notifies the helper.
- Check for an earlier thrown error that prevented open_application from completing.
Example fix
// before
const session = createBackend();
await session.preview(); // no app chosen yet
// after
const session = createBackend();
await session.open_application({ name: 'Finder' });
await session.preview(); Defensive patterns
Strategy: validation
Validate before calling
if (!session.inputApp) {
await session.open_application({ name: targetAppName });
}
await session.preview(); Type guard
null
Try / catch
try {
await session.preview();
} catch (e) {
if (String(e.message).includes('open_application first')) {
await session.open_application({ name: 'Finder' });
await session.preview();
} else throw e;
} Prevention
- Treat open_application as step one of every session that uses preview.
- Wrap session setup in a helper that enforces call order.
- Check state.inputApp (or track it in your wrapper) before toggling preview.
When it happens
Trigger: Calling preview() or preview({enabled:true}) as the first action in a session, or after the preview app was never opened, instead of calling open_application first.
Common situations: Scripts that enable preview at startup before selecting a target app; sessions where an earlier error aborted open_application so state.inputApp was never set.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- background preview capture failed
- element does not belong to the bound application —…
- open_application first — invoke_menu acts on the bound…
- open_application first to choose which application receives…
- A cancelled Operation cannot be edited.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e9b49b5e8e14bcfd.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:995
} else {
t.ocr = { status: "unavailable", engine: "apple_vision", reason: ocr?.reason ?? "The native OCR helper needs an update or returned mismatched image dimensions", blocks: [], raster };
}
} catch (error) {
throwIfAborted();
if (error.code === "cancelled") throw error;
t.ocr = { status: "unavailable", engine: "apple_vision", reason: error.message, blocks: [], ...(raster ? { raster } : {}) };
}
}
return t;
},
resolve_element: async ({ app_ref, windowIndex, path: pathArr } = {}) => {
const r = await native("resolve_element", { app_ref, windowIndex: windowIndex ?? 0, path: pathArr ?? [] });
return { found: !!r?.found, element: r?.element ?? null, reason: r?.reason ?? null };
},
preview: async ({ enabled = true } = {}) => {
state.previewEnabled = enabled;
if (!enabled) { stopPreviewLoop(); await quiescePreview(); await native("preview_notify", { enabled: false }); return { enabled: false }; }
if (!state.inputApp) throw new ExecError("open_application first to choose the preview app");
return updatePreview(true);
},
screenshot,
zoom,
left_click: async ({ target, strategy = "auto" } = {}) => {
if (target?.type !== "element" || strategy === "event" || strategy === "app") return pointerClick("left", target?.x, target?.y, 1, strategy);
if (!["auto", "a11y"].includes(strategy)) throw new ExecError(`strategy must be auto, a11y, app or event (got ${JSON.stringify(strategy)})`);
try {
assertBoundElement(target);
if ((await native("input_capabilities"))?.element_identity !== 1) throw new ExecError("native helper needs an update for element identity validation");
const semantic = ["AXTextField", "AXTextArea", "AXComboBox", "AXRow", "AXCell", "AXMenuItem"].includes(target.role);
if (semantic) await requireBackgroundActions();
const receipt = await native(semantic ? "click_element" : "perform_action", { target, action: "AXPress" });
if (!receipt?.action_sent) throw new ExecError("element press was not acknowledged");
return { ...receipt, action: receipt.action ?? "AXPress", strategy: "a11y", pointer_moved: false,
element: { role: target.role, label: target.label ?? null }, verified: receipt.verified ?? false, verification_required: "observation" };
} catch (error) {
// An AX frame can cover other controls. Never turn a refused orView on GitHub (pinned to 73e0f67d83)