Skip to content

[Bug]: macOS native capture silently discards the webcam track on long recordings #253

Description

@lorenjphillips

Search existing issues

  • I have searched the existing issues

Describe the bug

On the macOS native capture path the webcam is buffered entirely in memory and only written at finalize. Past ~2 GB the finalize step throws, the error is swallowed, and the session is saved screen-only. No toast, no failure state — the editor opens normally with the camera track simply absent.

A 23-minute take (webcam at BITRATE_BASE 18 Mbps ≈ 2.9 GB) lost the webcam. Shorter takes in the same app session saved fine.

Cause, current on main:

1. The native path builds the webcam handle with no fileName, selecting buffering — nothing reaches disk during capture:

// src/hooks/useScreenRecorder.ts:1323
nativeWebcamRecorder = createRecorderHandle(webcamStream.current, {
  mimeType: selectMimeType(),
  videoBitsPerSecond: BITRATE_BASE,
});
// src/hooks/recorderHandle.ts:43
let mode: "pending" | "streaming" | "buffering" = fileName ? "pending" : "buffering";

The legacy path at useScreenRecorder.ts:1771 passes the filename and streams to disk, so it is unaffected.

2. Finalize flattens the whole recording into one ArrayBuffer for IPC:

// src/hooks/useScreenRecorder.ts:605-613
const fixedWebcamBlob = await fixWebmDuration(webcamBlob, duration + webcamHeadStartMs);
return { videoData: await fixedWebcamBlob.arrayBuffer(), ... };
} catch (error) {
  console.error("Failed to finalize native macOS webcam recording:", error);
  return undefined;
}

3. undefined makes the guard at :639 skip attachNativeMacWebcamRecording, so the session is written with no webcamVideoPath. Only console.error fires; the user is never told.

Expected behavior

Stream the webcam to disk on the native path as the legacy path already does. Failing that, surface a toast when the webcam fails to attach — silently discarding a completed take is the worst available outcome.

To Reproduce

  1. macOS, webcam enabled, start a recording (native capture).
  2. Record 20+ minutes.
  3. Stop.
  4. Editor opens with screen + audio, no camera, no error.

Screenshots

No response

OS

macOS

OS Version

macOS 26.5.2

Browser

None

Device Type

Laptop

Additional context

Session file written for the failed take:

{ "screenVideoPath": ".../recording-<id>.mp4", "createdAt": <id>, "cursorCaptureMode": "editable-overlay" }

versus a short take from the same app session:

{ "screenVideoPath": ".../recording-<id>.mp4", "webcamVideoPath": ".../recording-<id>-webcam.webm", ... }

The camera streamed for the full take — macOS cameracaptured, recording ran 09:29:54–09:52:56:

09:28:53.592  -[CMIOExtensionProvider startStreamForClientID:streamID:reply:]
09:52:56.942  -[CMIOExtensionProvider stopStreamForClientID:streamID:reply:]

The main process crossed the 2 GB file-backed write threshold mid-recording, consistent with Chromium paging the buffered blob to blob_storage:

Event:   disk writes
Writes:  2149.48 MB of file backed memory dirtied over 1564 seconds,
         exceeding limit of 24.86 KB per second over 86400 seconds

Same machine, same webcam, one app session:

Take Webcam file
6.4s 6 MB
7.6s 9 MB
176.8s 267 MB
196.8s 277 MB
1381.5s none

Observed on 1.7.0; the code paths above are current on main. Plausibly the same root cause as #41 — "something failed but it was silent".

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions