{"record":{"id":"834977f54d69d496","repo":"remotion-dev/remotion","slug":"onprogress-must-be-a-function","errorCode":null,"errorMessage":"onProgress must be a function.","messagePattern":"onProgress must be a function\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/video-matting/src/separate-video-layers.ts","lineNumber":225,"sourceCode":"\n\tresolveVideoMattingQuality(options.videoBitrate ?? 'very-high');\n\tresolveVideoMattingQuality(options.audioBitrate ?? 'medium');\n\n\tif (\n\t\toptions.keyframeIntervalInSeconds !== undefined &&\n\t\t(!Number.isFinite(options.keyframeIntervalInSeconds) ||\n\t\t\toptions.keyframeIntervalInSeconds <= 0)\n\t) {\n\t\tthrow new TypeError(\n\t\t\t'keyframeIntervalInSeconds must be a positive finite number.',\n\t\t);\n\t}\n\n\tif (\n\t\toptions.onProgress !== undefined &&\n\t\ttypeof options.onProgress !== 'function'\n\t) {\n\t\tthrow new TypeError('onProgress must be a function.');\n\t}\n\n\tif (\n\t\toptions.onModelLoadProgress !== undefined &&\n\t\ttypeof options.onModelLoadProgress !== 'function'\n\t) {\n\t\tthrow new TypeError('onModelLoadProgress must be a function.');\n\t}\n};\n\nconst makeInput = (src: string | URL | Blob): Input => {\n\tconst source =\n\t\ttypeof src === 'string' || src instanceof URL\n\t\t\t? new UrlSource(src)\n\t\t\t: new BlobSource(src);\n\n\treturn new Input({formats: ALL_FORMATS, source});\n};","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/video-matting/src/separate-video-layers.ts#L207-L243","documentation":"validateOptions for separateVideoLayers() verifies the optional onProgress callback is actually a function; since progress reporting is invoked during the long separation run, a non-function value would crash later and is rejected during option validation.","triggerScenarios":"Passing onProgress: true, an object, a stringified function, or a variable that is undefined-but-assigned (e.g. a JSON-parsed config where functions cannot survive serialization).","commonSituations":"Loading options from JSON (functions don't serialize); passing console.log.bind result incorrectly; typing the value into a config file.","solutions":["Pass an actual function: onProgress: (p) => console.log(p)","Remove the onProgress key if you don't need progress updates","If options come from JSON/config, attach callbacks after parsing"],"exampleFix":"// before\nawait separateVideoLayers({ src, outputs, onProgress: config.onProgress }); // undefined-ish object from JSON\n// after\nawait separateVideoLayers({ src, outputs, onProgress: (progress) => console.log(Math.round(progress * 100) + '%') });","handlingStrategy":"type-guard","validationCode":"if (onProgress !== undefined && typeof onProgress !== 'function') throw new TypeError('onProgress must be a function');","typeGuard":"const isFunction = (v) => typeof v === 'function';","tryCatchPattern":"try { await separateVideoLayers(opts); } catch (e) { if (e instanceof TypeError && e.message.includes('onProgress must be a function')) { /* attach a real callback */ } else throw e; }","preventionTips":["Never round-trip callbacks through JSON","Pass arrow functions directly at the call site","Remove the key instead of passing a falsy non-function"],"tags":["validation","callback","type-mismatch"],"backgroundTag":"invalid-argument-value","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-09-09T13:27:51.281Z","contentChangedAt":"2026-09-09T13:27:51.281Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}