{"record":{"id":"8bc8544984fb556e","repo":"Hmbown/CodeWhale","slug":"unknown-or-already-finished-recording-id","errorCode":null,"errorMessage":"unknown or already-finished recording \"${id}\"","messagePattern":"unknown or already-finished recording \"(.+?)\"","errorType":"exception","errorClass":"ExecError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/backends/darwin.mjs","lineNumber":687,"sourceCode":"        completion.then(result => { if (!ready) reject(new ExecError(result.error || \"screen recorder exited before capture started\")); });\n      });\n      throwIfAborted();\n      return { id, pid: child.pid, file, display: disp, durationSec: durationSec ?? null, region: region ?? null, fps: 30, mode: \"ScreenCaptureKit\", startedAt,\n        ...(window ? { window: { id: window.window_id ?? null, name: window.name ?? null }, note: \"Recording the window's rect as it was at start; it does not track moves or resizes.\" } : {}) };\n    } catch (error) {\n      requestRecordingStop(recording);\n      const result = await waitForRecordingStop(recording, 2_000);\n      if (result.terminated !== false) rec.delete(id);\n      throw error;\n    } finally {\n      clearTimeout(timer);\n      signal?.removeEventListener(\"abort\", abort);\n    }\n  }\n\n  async function recordingStop({ id }) {\n    const r = rec.get(id);\n    if (!r) throw new ExecError(`unknown or already-finished recording \"${id}\"`);\n    requestRecordingStop(r);\n    const result = await waitForRecordingStop(r, 20_000);\n    if (result.code !== 0) throw new ExecError(result.error || \"screen recorder failed; partial file retained\");\n    const size = fs.existsSync(r.file) ? fs.statSync(r.file).size : 0;\n    if (!size) throw new ExecError(\"screen recorder produced no video\");\n    rec.delete(id);\n    return { id, file: r.file, mp4: null, bytes: size, mode: r.mode, startedAt: r.startedAt, stoppedAt: new Date().toISOString() };\n  }\n\n  async function closeSession() {\n    // The preview this session showed must not outlive the session; a panel\n    // from a dead session has no owner to refresh or hide it.\n    stopPreviewLoop();\n    await quiescePreview();\n    if (state.previewEnabled && state.inputApp) {\n      try { await native(\"preview_notify\", { enabled: false }); } catch { /* hiding is best-effort */ }\n    }\n    state.previewEnabled = false;","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/backends/darwin.mjs#L669-L705","documentation":"recordingStop({id}) looks up the recording handle in an in-memory map; this error means the id is not present, so it was never started by this session or its stop already completed (the map entry is deleted on successful stop). The library throws rather than guessing which stale or fabricated id you meant.","triggerScenarios":"Calling screen-recording stop with an id returned by a previous session, an id already stopped successfully (the entry is deleted from the rec map at the end of recordingStop), a typo'd id, or after the plugin/session was restarted and its in-memory recording registry was cleared.","commonSituations":"An agent retries a stop call after a first stop already succeeded; the TUI restarted between recording start and stop so all ids became invalid; the id was hallucinated or copied from a log of an old session.","solutions":["Check the id against the value returned by the recording start call in the CURRENT session.","If the stop may have already succeeded, treat the recording as stopped and read the file from the previously returned result instead of stopping again.","If the session restarted, the recording is unmanageable via this API; re-start a new recording if one is still needed.","Catch the error and call the recording status/inspect surface to confirm no live recording with that id exists before retrying."],"exampleFix":"// before\nawait backend.recordingStop({ id: rememberedIdFromOldSession });\n// after\nif (sessionRecording && sessionRecording.id === id) {\n  const out = await backend.recordingStop({ id });\n}","handlingStrategy":"try-catch","validationCode":"if (!currentRecording || currentRecording.id !== id) {\n  throw new Error(`no live recording id ${id} in this session`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const out = await backend.recordingStop({ id });\n} catch (e) {\n  if (String(e.message).includes('unknown or already-finished recording')) {\n    // already stopped or stale id — treat as no-op, use retained file\n  } else throw e;\n}","preventionTips":["Keep the recording id from the start call in session-scoped state only.","Treat stop as idempotent-by-catch: a second stop should be a no-op, not a retry.","Never persist recording ids across plugin/session restarts."],"tags":["recording","invalid-id","stateful-api"],"backgroundTag":"record-not-found","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}