{"record":{"id":"59653d6ca8295546","repo":"remotion-dev/remotion","slug":"no-canvas-recording-was-available-to-open","errorCode":null,"errorMessage":"No canvas recording was available to open.","messagePattern":"No canvas recording was available to open\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/canvas-capture-extension/src/capture.ts","lineNumber":328,"sourceCode":"\t\t\tthis.#wrapped.canvas,\n\t\t\twidth,\n\t\t\theight,\n\t\t\twindow.devicePixelRatio,\n\t\t);\n\t\tawait this.#recorder.startRecording();\n\t\tthis.#requestPaint();\n\t};\n\n\tstop = async () => {\n\t\ttry {\n\t\t\tif (this.#paintError) {\n\t\t\t\tthrow this.#paintError;\n\t\t\t}\n\n\t\t\tthis.#draw();\n\t\t\tconst file = await this.#recorder.stopRecording();\n\t\t\tif (!file) {\n\t\t\t\tthrow new Error('No canvas recording was available to open.');\n\t\t\t}\n\n\t\t\treturn file;\n\t\t} finally {\n\t\t\tthis.restore();\n\t\t}\n\t};\n\n\trestore = () => {\n\t\tif (this.#restored) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#restored = true;\n\t\tthis.#resizeObserver.disconnect();\n\t\tthis.#wrapped.canvas.removeEventListener('paint', this.#onPaint);\n\t\tthis.#recorder.dispose();\n\t\tthis.#wrapped.restore();","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/canvas-capture-extension/src/capture.ts#L310-L346","documentation":"Thrown by `CaptureSession.stop()` after `this.#recorder.stopRecording()` resolves to a falsy file. Before throwing, the recorder flushes any pending paint error and runs a final `#draw()`. The `finally` block calls `this.restore()`, so the wrapped page is always unwound. A null/undefined file means the recorder produced no output blob.","triggerScenarios":"Calling `stop()` immediately after `start()` before any frames were drawn; the recorder's `finalizeRecording` returned null/undefined (e.g., Mediabunny produced no buffer and that path returned falsy); the recorder was already stopped or disposed so no new file is produced.","commonSituations":"Start/stop called in rapid succession with no paint cycles between them; capture started but the page never repainted; a prior error path left the recorder in a state where `stopRecording()` resolves empty.","solutions":["Ensure at least one frame is captured before calling `stop()` — wait for a paint or a timeout.","Check `this.#paintError` was not set; if it was, the underlying paint failure is the root cause.","Do not call `stop()` twice or after `restore()`/dispose.","If the recorder returns no file persistently, inspect Mediabunny encoding errors surfaced via `recording.encodingError`."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ensure at least one frame is drawn before stopping.\nasync function safeStop(rec: {stop: () => Promise<File | null>}, drewFrame: boolean) {\n  if (!drewFrame) throw new Error('Wait for at least one frame before stopping.');\n  return rec.stop();\n}","typeGuard":null,"tryCatchPattern":"try {\n  const file = await session.stop();\n} catch (e) {\n  if (e instanceof Error && /No canvas recording was available/.test(e.message)) {\n    await waitForNextPaint();\n    // do NOT blindly retry; re-arm the session instead.\n  }\n  throw e;\n}","preventionTips":["Wait for a paint cycle between start and stop.","Never call stop() twice on the same session.","Surface paint errors before stopping."],"tags":["canvas-capture","recorder","no-output","lifecycle"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}