{"record":{"id":"939cf19999a48b15","repo":"remotion-dev/remotion","slug":"htmlincanvas-when-oninit-is-provided-it-must-r","errorCode":null,"errorMessage":"HtmlInCanvas: when `onInit` is provided, it must return a cleanup function, or a Promise that resolves to one.","messagePattern":"HtmlInCanvas: when `onInit` is provided, it must return a cleanup function, or a Promise that resolves to one\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/HtmlInCanvas.tsx","lineNumber":527,"sourceCode":"\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tinitializedRef.current = true;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (paintTarget instanceof HTMLCanvasElement) {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t'HtmlInCanvas: onInit requires an OffscreenCanvas paint target',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst cleanup = await currentOnInit({\n\t\t\t\t\t\t\t\tcanvas: paintTarget,\n\t\t\t\t\t\t\t\telement,\n\t\t\t\t\t\t\t\telementImage: initImage,\n\t\t\t\t\t\t\t\tpixelDensity: resolvedPixelDensity,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tif (typeof cleanup !== 'function') {\n\t\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t\t'HtmlInCanvas: when `onInit` is provided, it must return a cleanup function, or a Promise that resolves to one.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (unmountedRef.current) {\n\t\t\t\t\t\t\t\tcleanup();\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tonInitCleanupRef.current = cleanup;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tinitImage.close();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet elImage: ElementImage;\n\t\t\t\ttry {\n\t\t\t\t\telImage = placeholderCanvas.captureElementImage(element);","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/core/src/HtmlInCanvas.tsx#L509-L545","documentation":"Thrown inside the onPaint callback of <HtmlInCanvas> when the user-provided `onInit` resolves to a value that is not a function. The contract for onInit is to return a cleanup callback (or a Promise of one) so that Remotion can release paint-target resources (WebGPU device, buffers, textures) when the component unmounts or the canvas remounts. A missing cleanup leaks GPU/canvas state.","triggerScenarios":"Implementing `onInit` that returns undefined, null, a non-function value, or forgets the `return` statement; returning a Promise that resolves to a non-function; arrow functions that implicitly return undefined.","commonSituations":"First-time onInit authors who treat it like onPaint (which is void); async init that sets up a WebGPU device but never returns a destroy callback; refactoring onInit and dropping the return line.","solutions":["Make your onInit return a no-op cleanup if there is nothing to release: `return () => {};`.","Return the actual teardown: `return () => { device.destroy(); ctx.unmap(); }`.","If onInit is async, ensure the resolved value is a function: `return Promise.resolve(() => {})` or `return asyncCleanup;`.","Lint your onInit with an explicit `return` statement (eslint: consistent-return)."],"exampleFix":"// before\nconst onInit = async ({canvas}) => {\n  const device = await adapter.requestDevice();\n  // no return\n};\n// after\nconst onInit = async ({canvas}) => {\n  const device = await adapter.requestDevice();\n  return () => device.destroy();\n};","handlingStrategy":"validation","validationCode":"const result = await onInit(params);\nif (typeof result !== 'function') {\n  throw new TypeError('onInit must return a cleanup function');\n}\nreturn result;","typeGuard":"const isCleanup = (v: unknown): v is () => void => typeof v === 'function';","tryCatchPattern":"try {\n  const cleanup = await onInit(params);\n  if (typeof cleanup !== 'function') {\n    throw new Error('onInit did not return a cleanup');\n  }\n  // store cleanup\n} catch (err) {\n  // report a user-facing error about onInit contract\n  throw err;\n}","preventionTips":["Always return a function from onInit, even a no-op `() => {}`.","Add an eslint `consistent-return` rule on onInit handlers.","Type onInit return as `() => void` or `Promise<() => void>`.","Unit-test onInit to assert the resolved value is callable."],"tags":["html-in-canvas","oninit","lifecycle","contract","webgpu"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}