Hmbown/CodeWhale · warning · ExecError

no display ; have [ ]

Error message

no display ${index}; have [${ds.map((d) => d.index).join(", ")}]

What it means

switch_display validates the requested display index against the currently enumerated displays (from displayInfo/list_displays); if no display has that index it throws, listing the valid indices. Indices are re-enumerated on every call, so the set reflects current hardware state.

Solutions

  1. Call list_displays and use one of the indices it returns (the error message lists them too).
  2. Handle the error and fall back to the first available display when the requested one is gone.
  3. Re-verify displays after hardware changes (dock connect/disconnect, sleep/wake) before switching.

Example fix

// before
await backend.switchDisplay({ index: 2 });
// after
const ds = await backend.listDisplays();
if (ds.some(d => d.index === 2)) await backend.switchDisplay({ index: 2 });
Defensive patterns

Strategy: validation

Validate before calling

const ds = await backend.listDisplays();
if (!ds.some(d => d.index === index)) throw new Error(`display ${index} not available; have ${ds.map(d => d.index)}`);

Try / catch

try {
  await backend.switchDisplay({ index });
} catch (e) {
  if (String(e.message).startsWith('no display')) {
    const ds = await backend.listDisplays();
    if (ds.length) await backend.switchDisplay({ index: ds[0].index });
  } else throw e;
}

Prevention

When it happens

Trigger: Calling switch_display with an index that does not exist — a 0-based vs 1-based confusion, an index from a previous session whose monitor setup changed, or an index hallucinated without calling list_displays.

Common situations: Docking-station changes removing a second display; scripts hardcoded to a two-monitor setup run on a single-monitor machine; off-by-one errors between display numbering conventions.

Related errors


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

Appendix: source

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

    caps.raw_input = caps.accessibility_tree;
    try {
      const t = os.tmpdir() + `/cu-probe-${crypto.randomBytes(3).toString("hex")}.png`;
      const r = await runL("screencapture", ["-x", "-R0,0,2,2", "-t", "png", t], { timeoutMs: 8_000 });
      perms.screen_capture = r.code === 0 ? "ok" : "failed";
      try { fs.rmSync(t, { force: true }); } catch {}
    } catch { perms.screen_capture = "failed"; }
    caps.screenshot = perms.screen_capture === "ok";
    caps.recording = caps.screenshot;
    return { platform: "darwin", capabilities: caps, permissions: perms, note: "macOS does not expose Screen-Recording TCC state to CLI; a black/empty screenshot means Screen Recording permission is missing. Background mode (open_application activate:false) uses process-bound keyboard events and accessibility actions; shared pointer gestures are refused. Foreground control (activate:true) uses the shared desktop and requires exclusive use. Neither mode is an isolated desktop. App-specific behavior still requires verification." };
  }

  return {
    platform: "darwin",
    probe,
    list_displays: displayInfo,
    async switch_display({ index }) {
      const ds = await displayInfo();
      if (!ds.some((d) => d.index === index)) throw new ExecError(`no display ${index}; have [${ds.map((d) => d.index).join(", ")}]`);
      state.activeDisplay = index;
      return { activeDisplay: index };
    },
    list_apps: listApps,
    set_window_frame: async ({ app_ref, window_id, frame } = {}) => {
      if (!frame || !Number.isFinite(frame.x) || !Number.isFinite(frame.y) || !Number.isFinite(frame.w) || !Number.isFinite(frame.h) || frame.w <= 0 || frame.h <= 0) {
        throw Object.assign(new ExecError("set_window_frame needs frame {x,y,w,h} with positive w/h"), { code: "bad_args" });
      }
      if (!Number.isSafeInteger(window_id) || window_id < 0) {
        throw Object.assign(new ExecError("set_window_frame needs window_id (a non-negative window index from list_windows)"), { code: "bad_args" });
      }
      const r = await native("set_window_frame", { app_ref, window_id, frame });
      return { ...r, verified: r?.verified === true, note: r?.note ?? "the after frame is the app's own readback; cross-check with list_windows before relying on it" };
    },
    list_windows: listWindows,
    open_application: openApplication,
    get_app_state: async ({ app_ref, detail, depth, window_id, include_ocr = false, ocr_region } = {}) => {
      const t0 = Date.now();

View on GitHub (pinned to 73e0f67d83)