Skip to content

Recording SDK can start capture after its React component unmounts #2794

Closed

Description

@AksharP5

Description

Closing a React recorder component while camera or microphone permission is pending does not cancel startup. If the permission request later succeeds, @cap/sdk-recorder starts MediaRecorder after the hook has unmounted. The capture track stays live without recorder controls on the page.

There is a second consequence of the same ownership gap. If screen sharing has already been granted and the microphone prompt is still pending, destroy() leaves the acquired screen track live. The recorder only stores those streams after all permission requests finish.

Expected: destroying a recorder releases acquired tracks immediately, releases any tracks returned later, and prevents that start from creating a recorder or changing a replacement recording.

Actual: a late grant resumes startup and changes the destroyed recorder back to recording. A late denial can also clean up a new recording started on the same instance.

Reproduction

  1. Mount a component that uses the following hook and calls start() from a button:
const recorder = useCapRecorder({
	publicKey: "YOUR_SDK_PUBLIC_KEY",
	apiBase: "https://cap.so",
	mode: "camera",
	microphone: { enabled: false },
});
  1. Leave the camera permission request pending. Unmount the component, for example by closing its recorder modal.
  2. Allow the pending camera request.
  3. Observe that the camera track stays live and MediaRecorder starts after unmount. The hook cleanup already called destroy(), so the component has no controls for this recording.

The class API shows the same sequence with const starting = recorder.start(); recorder.destroy();, followed by the browser granting the pending request.

Additional Context

  • Baseline main: d27360a8ac928ba15bfbaec75236762f922d2e52, package version 0.1.0.
  • Verified with React 19.2.8 and Chrome 154 on Linux x86_64. Real React DOM, MediaRecorder and MediaStreamTrack lifecycle APIs were used. Device acquisition was a deferred fixture returning a canvas stream. The screen and microphone case used a canvas stream and an AudioContext destination stream. Physical devices and native permission prompts were not exercised.
  • Before: a late camera grant leaves one real MediaRecorder in recording with a live track. After cancellation is carried through startup: no MediaRecorder is constructed and the track is ended.
  • The screen track is live immediately after destroy() on main while microphone permission is pending. With cancellation it is immediately ended, and the late microphone track also ends.
  • No cloud requests are needed to reproduce startup. The successful recording control used HTTP response fixtures.

Relevant source: start and destroy, stream acquisition, and React cleanup.

Activity

  1. linear commented on Oct 21, 2026

    @linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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