remotion-dev/remotion · error
renderMediaOnWeb() was cancelled
Error message
renderMediaOnWeb() was cancelled
What it means
renderMediaOnWeb() checks the AbortSignal passed via the `signal` option right after starting the mediabunny output. If the caller aborted the signal before or during output.start(), the renderer throws this error instead of continuing to encode frames, so an aborted render stops early and never produces a partially-usable output file. It is Remotion's cancellation contract for browser-side (WebCodecs) renders.
Solutions
- Check why the AbortController was aborted before treating this as a bug — the error confirms a deliberate cancel, not a codec failure.
- If the cancel was unintentional (e.g. React StrictMode double-effect), don't abort in the first effect's cleanup while the render is still needed, or gate the abort on the effect actually being disposed.
- If you expect cancellations, catch this error specifically and treat it as a normal, expected outcome rather than logging it as a failure.
- If you want a fresh attempt after an abort, create a new AbortController and call renderMediaOnWeb() again; the cancelled render cannot be resumed.
- Move the abort later: if you only intend to cancel slow renders, start the abort timer after the first onProgress callback instead of at t=0.
Example fix
// before
useEffect(() => {
renderMediaOnWeb({composition, signal: controller.signal});
return () => controller.abort(); // aborts the in-flight render on unmount/StrictMode
}, []);
// after
useEffect(() => {
const controller = new AbortController();
renderMediaOnWeb({composition, signal: controller.signal})
.catch((err) => {
if (err?.message === 'renderMediaOnWeb() was cancelled') return;
throw err;
});
return () => controller.abort();
}, []); Defensive patterns
Strategy: try-catch
Validate before calling
// before calling renderMediaOnWeb, ensure the controller is not already aborted
if (controller.signal.aborted) {
throw new Error('Skipping render: signal already aborted');
}
renderMediaOnWeb({composition, signal: controller.signal}); Type guard
const isAbortError = (err: unknown): boolean => err instanceof Error && err.message === 'renderMediaOnWeb() was cancelled';
Try / catch
try {
await renderMediaOnWeb({composition, signal: controller.signal});
} catch (err) {
if (isAbortError(err)) {
// expected cancel: clean up and return
return null;
}
throw err;
} Prevention
- Create the AbortController inside the same effect/function that starts the render so its lifecycle matches.
- Avoid aborting in React StrictMode double-invoked effect cleanups when the render should survive remounts.
- Treat this specific message as an expected outcome and filter it from error reporting/telemetry.
- Never reuse an aborted AbortSignal for a new render; create a fresh controller per attempt.
- Add a cancellation test: abort mid-render and assert this error surfaces and no partial output is consumed.
When it happens
Trigger: Calling renderMediaOnWeb({..., signal}) and either (a) aborting the AbortController before the call's internal output.start() promise resolves, or (b) calling controller.abort() concurrently very early in the render, racing ahead of the post-start checkpoint at render-media-on-web.tsx:513.
Common situations: UI code that lets the user cancel a browser render and aborts immediately after starting; React effects whose cleanup aborts the controller on unmount/StrictMode double-mount, cancelling an in-flight render; a timeout firing during the output startup phase; a page navigation or component remount tearing down the controller while renderMediaOnWeb is still bootstrapping.
Related errors
- Cannot call `getInputProps()` - window.remotion_inputProps…
- Cannot cancel render
- Cannot cancel render #
- Cannot cancel render
- Cannot cancel render
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/cce4eee4c559e918.
Report an issue: GitHub.
Appendix: source
Thrown at packages/web-renderer/src/render-media-on-web.tsx:513
: null,
bitrate: resolvedAudioBitrate,
});
if (audioSampleSource) {
outputWithCleanup.output.addAudioTrack(
audioSampleSource.audioSampleSource,
{
// ~1 packet per 10ms, + 33% buffer
// https://mediabunny.dev/api/BaseTrackMetadata#maximumpacketcount
maximumPacketCount: Math.ceil(durationInSeconds * 100 * 1.33),
},
);
}
await outputWithCleanup.output.start();
if (signal?.aborted) {
throw new Error('renderMediaOnWeb() was cancelled');
}
const audioMixer = createAudioMixer({fps: resolved.fps, sampleRate});
const encodeReadyAudio = async () => {
if (!audioSampleSource) {
return;
}
let audio: AudioData | null;
while ((audio = audioMixer.getReadyAudio())) {
try {
await addAudioSample(audio, audioSampleSource.audioSampleSource);
} finally {
audio.close();
}
await waitForPageResponsiveness();
}View on GitHub (pinned to 46a3a6bf13)