Hmbown/CodeWhale · error · ExecError
screen recorder failed; partial file retained
Error message
screen recorder failed; partial file retained
What it means
After the stop is requested, recordingStop waits up to 20s for the underlying macOS screen-recorder child process to exit; if the child exits with a nonzero code the recorder failed, and the library throws this error while deliberately keeping the partial file on disk. The recorder's own stderr text (result.error) is preferred in the message when available.
Solutions
- Read the thrown message / result.error for the recorder's specific stderr and fix that root cause first.
- Verify Screen Recording permission is still granted to the host app (System Settings > Privacy & Security > Screen Recording).
- Inspect the retained partial file at the path from the start call — it may still be usable.
- Re-start the recording and retry; if it recurs, run the recorder command manually to see the raw failure.
Example fix
null
Defensive patterns
Strategy: fallback
Validate before calling
null
Try / catch
try {
const out = await backend.recordingStop({ id });
} catch (e) {
console.error('recorder failed:', e.message);
// partial file is retained at the path from the start call — attempt to use it
} Prevention
- Grant and periodically re-verify Screen Recording permission for the host app.
- Check disk space before starting long recordings.
- Log the retained partial file path from the start result for post-mortems.
- Avoid sleeping/suspending the machine during active recordings.
When it happens
Trigger: Calling recording stop when the recorder process (e.g. screencapture/ffmpeg spawned at start) crashed, was killed, failed due to lost screen-recording permission, or hit a disk/encoder error, so its exit code is nonzero.
Common situations: Screen Recording permission revoked in System Settings mid-recording; recorder binary missing or incompatible after an OS update; encoder failure on very long recordings; machine sleep or user switching displays killing the capture session.
Related errors
- screen recorder produced no video
- The update could not be unpacked.
- application not found — call list_apps for exact names/pids
- AT-SPI action failed
- background preview capture failed
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/b762798fc097ce9d.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/plugins/computer-use/src/backends/darwin.mjs:690
return { id, pid: child.pid, file, display: disp, durationSec: durationSec ?? null, region: region ?? null, fps: 30, mode: "ScreenCaptureKit", startedAt,
...(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." } : {}) };
} catch (error) {
requestRecordingStop(recording);
const result = await waitForRecordingStop(recording, 2_000);
if (result.terminated !== false) rec.delete(id);
throw error;
} finally {
clearTimeout(timer);
signal?.removeEventListener("abort", abort);
}
}
async function recordingStop({ id }) {
const r = rec.get(id);
if (!r) throw new ExecError(`unknown or already-finished recording "${id}"`);
requestRecordingStop(r);
const result = await waitForRecordingStop(r, 20_000);
if (result.code !== 0) throw new ExecError(result.error || "screen recorder failed; partial file retained");
const size = fs.existsSync(r.file) ? fs.statSync(r.file).size : 0;
if (!size) throw new ExecError("screen recorder produced no video");
rec.delete(id);
return { id, file: r.file, mp4: null, bytes: size, mode: r.mode, startedAt: r.startedAt, stoppedAt: new Date().toISOString() };
}
async function closeSession() {
// The preview this session showed must not outlive the session; a panel
// from a dead session has no owner to refresh or hide it.
stopPreviewLoop();
await quiescePreview();
if (state.previewEnabled && state.inputApp) {
try { await native("preview_notify", { enabled: false }); } catch { /* hiding is best-effort */ }
}
state.previewEnabled = false;
await browser.close().catch(() => {});
const owned = [...rec.entries()];
for (const [, recording] of owned) requestRecordingStop(recording);View on GitHub (pinned to 73e0f67d83)