Hmbown/CodeWhale · error · ExecError

choose one available display for recording

Error message

choose one available display for recording

What it means

After resolving the effective display (explicit `display`, remembered activeDisplay, or host display of the window rect), the record action requires that display to exist in the current display list. If the resolved index matches no attached display, recording cannot proceed and the library throws.

Solutions

  1. Refresh with list_displays and pass one of the returned `index` values as display.
  2. Clear/omit the display option so recording falls back to a currently attached display.
  3. Update any persisted activeDisplay state after monitor changes.

Example fix

// before
await record({ display: staleIndex });
// after
const displays = await list_displays();
await record({ display: displays[0].index });
Defensive patterns

Strategy: validation

Validate before calling

const displays = await list_displays();
const idx = display ?? displays[0]?.index;
if (!displays.some(d => d.index === idx))
  throw new Error(`display ${idx} not attached; have [${displays.map(d => d.index)}]`);

Type guard

const displayExists = (idx, displays) => displays.some(d => d.index === idx);

Try / catch

try { await record({ display }) } catch (e) {
  if (String(e.message).includes("choose one available display")) {
    const displays = await list_displays();
    await record({ display: displays[0].index });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling record({ display: N }) where N is not an index of an attached display; a stale state.activeDisplay referencing a monitor that was disconnected; index 0 passed to a 1-based display list.

Common situations: Undocking a laptop between sessions leaves the old activeDisplay; agents pass display ids instead of indexes; headless adapters present when configured but not at record time.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/aed6f9b72c878206. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:636

    // app_ref scopes the recording to the app's window rect: resolved once at
    // start through the same window_info the AX path uses, so a background
    // window records behind the user's work. The rect is fixed at start —
    // it does not track later moves or resizes.
    let window = null;
    if (app_ref !== undefined || window_id != null) {
      window = await native("window_info", { app_ref: app_ref === undefined ? state.inputApp ?? undefined : app_ref, window_id });
      if (!window?.points || !(window.points.w > 0) || !(window.points.h > 0)) throw new ExecError("the selected application has no capturable window — call list_windows");
      if (region) throw new ExecError("choose app_ref or region, not both");
      region = [window.points.x, window.points.y, window.points.w, window.points.h];
    }
    let disp = display ?? state.activeDisplay;
    if (window && display == null) {
      const cx = region[0] + region[2] / 2, cy = region[1] + region[3] / 2;
      const host = displays.find(d => d.points && cx >= d.points.x && cx < d.points.x + d.points.w && cy >= d.points.y && cy < d.points.y + d.points.h);
      if (host) disp = host.index;
    }
    const selected = displays.find(d => d.index === disp);
    if (!selected) throw new ExecError("choose one available display for recording");
    if (durationSec != null && (!Number.isFinite(durationSec) || durationSec <= 0)) throw new ExecError("durationSec must be positive");
    const capabilities = await native("input_capabilities");
    if (capabilities?.record_owner_pipe !== 1) throw new ExecError("native screen recorder cannot own its client lifetime; update Computer Use before recording");
    const helper = await nativeHelper();
    throwIfAborted();
    const child = spawn(helper, [JSON.stringify({ tool: "record", args: { file, displayID: selected.id, region, durationSec, owner_pipe: true } })], { stdio: ["pipe", "pipe", "pipe"] });
    child.stdin.on("error", () => {});
    const startedAt = new Date().toISOString();
    let stderr = "", output = "", ready = false;
    const completion = new Promise(resolve => {
      child.once("error", error => resolve({ code: -1, error: error.message }));
      child.once("close", code => resolve({ code, error: stderr.trim() }));
    });
    child.stderr.on("data", chunk => { stderr = (stderr + chunk).slice(-4000); });
    const recording = { child, completion, pid: child.pid, file, startedAt, mode: "ScreenCaptureKit", display: disp };
    rec.set(id, recording);
    const signal = currentSignal();
    let timer, abort;

View on GitHub (pinned to 73e0f67d83)