Hmbown/CodeWhale · error · ExecError

unknown recording

Error message

unknown recording "${id}"

What it means

recordingStop on the Linux backend is a stub: recording is unavailable on this platform (see recordingStart, which always throws), so stopping any id throws `unknown recording "<id>"`. There is no recording registry, so no id can ever succeed.

Solutions

  1. Guard recordingStop behind a recordingList/recordingStatus check that the id actually exists
  2. Skip recording lifecycle calls on Linux; use screenshot-based capture (the error in recordingStart recommends this)
  3. Track recording handles in your own code and only call stop for handles you successfully started

Example fix

// before
await backend.recordingStop(id);
// after
const recs = await backend.recordingList();
if (recs.some(r => r.id === id)) await backend.recordingStop(id);
Defensive patterns

Strategy: validation

Validate before calling

const list = await recordingList();
if (!list.some(r => r.id === id)) return; // nothing to stop

Try / catch

try { await recordingStop(id); } catch (e) { if (!/unknown recording/.test(e.message)) throw e; }

Prevention

When it happens

Trigger: Calling recordingStop with any recording id on Linux; calling it after a recordingStart attempt that itself always failed.

Common situations: Portable agent code that unconditionally calls recordingStop in cleanup/finally paths regardless of platform; code ported from the macOS backend where recordings exist.

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/7c521282a5472cdd. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/backends/linux.mjs:773

        : ["wl-copy"];
      need(cmd[0], "clipboard write");
      spawnDetached(cmd[0], cmd.slice(1), String(text ?? ""), true);
      return { written: String(text ?? "").length };
    },
    cursor_position: async () => {
      await probeSession();
      if (session === "x11") {
        const out = await xdotool(["getmouselocation"]);
        const m = /x:(-?\d+)\s+y:(-?\d+)/.exec(out);
        if (!m) throw new ExecError(`could not parse xdotool getmouselocation output: ${out}`);
        return { x: Number(m[1]), y: Number(m[2]) };
      }
      throw new ExecError("cursor position needs an X11 session in this build");
    },
    recordingStart: async () => {
      throw Object.assign(new ExecError("Recording is unavailable on this platform until the recorder has session-owned cleanup. Use screenshots instead."), { code: "owned_recording_unavailable" });
    },
    recordingStop: async ({ id }) => { throw new ExecError(`unknown recording "${id}"`); },
    recordingStatus: ({ id }) => ({ id, running: false }),
    recordingList: async () => {
      const dir = recordingsDir();
      const out = fs.existsSync(dir)
        ? fs.readdirSync(dir).filter((f) => /\.(mp4|mkv|png)$/i.test(f)).map((f) => {
            const st = fs.statSync(path.join(dir, f));
            return { file: path.join(dir, f), bytes: st.size, modifiedAt: st.mtime.toISOString() };
          }).sort((a, b) => b.modifiedAt.localeCompare(a.modifiedAt)).slice(0, 50)
        : [];
      return { dir, recordings: out, running: [] };
    },
  };

  async function inputChain(x, y, act) {
    await inputMove(x, y);
    await act();
    return { action_sent: true, at: { x: Number(x), y: Number(y) } };
  }

View on GitHub (pinned to 73e0f67d83)