{"record":{"id":"d7428221ea61f7cc","repo":"remotion-dev/remotion","slug":"src-must-be-a-string-url-or-blob","errorCode":null,"errorMessage":"src must be a string, URL, or Blob.","messagePattern":"src must be a string, URL, or Blob\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/video-matting/src/separate-video-layers.ts","lineNumber":163,"sourceCode":"\t\t}\n\n\t\tif (output.outputWritable.locked) {\n\t\t\tthrow new TypeError(\n\t\t\t\t`outputs.${layer}.outputWritable must not already be locked.`,\n\t\t\t);\n\t\t}\n\t}\n};\n\nconst validateOptions = (options: SeparateVideoLayersOptions) => {\n\tif (!options || typeof options !== 'object') {\n\t\tthrow new TypeError('separateVideoLayers() expects an options object.');\n\t}\n\n\tconst isBlob = typeof Blob !== 'undefined' && options.src instanceof Blob;\n\tconst isUrl = options.src instanceof URL;\n\tif (typeof options.src !== 'string' && !isUrl && !isBlob) {\n\t\tthrow new TypeError('src must be a string, URL, or Blob.');\n\t}\n\n\tif (typeof options.src === 'string' && options.src.length === 0) {\n\t\tthrow new TypeError('src must not be an empty string.');\n\t}\n\n\tif (\n\t\toptions.outputs !== undefined &&\n\t\t(!options.outputs ||\n\t\t\ttypeof options.outputs !== 'object' ||\n\t\t\tArray.isArray(options.outputs))\n\t) {\n\t\tthrow new TypeError('outputs must be an object.');\n\t}\n\n\tvalidateLayerOutputOptions({\n\t\tlayer: 'base',\n\t\toutput: options.outputs?.base,","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/video-matting/src/separate-video-layers.ts#L145-L181","documentation":"validateOptions for separateVideoLayers() requires options.src to identify the input video as a string path/URL, a URL object, or a Blob. Any other type (number, plain object, undefined) is rejected before the video is fetched or decoded.","triggerScenarios":"Passing an ArrayBuffer/Uint8Array of video bytes; passing undefined because the variable was misnamed; passing a File is fine (File extends Blob) but a plain object is not; passing a URL string built from non-string parts.","commonSituations":"Reading video into memory as bytes and assuming the API takes buffers; passing document.querySelector(...).src (DOM element instead of string); typed config from an unvalidated source.","solutions":["Pass a string path/URL, a new URL(...), or a Blob/File","Convert bytes to a Blob: new Blob([buffer], { type: 'video/mp4' })","Read the src from the correct variable/property (e.g. el.src instead of el)"],"exampleFix":"// before\nawait separateVideoLayers({ src: fileBuffer });\n// after\nawait separateVideoLayers({ src: new Blob([fileBuffer], { type: 'video/mp4' }) });","handlingStrategy":"type-guard","validationCode":"function assertSrc(src) { const isBlob = typeof Blob !== 'undefined' && src instanceof Blob; if (typeof src !== 'string' && !(src instanceof URL) && !isBlob) throw new TypeError('src must be a string, URL, or Blob'); }","typeGuard":"const isValidSrc = (v) => typeof v === 'string' || v instanceof URL || (typeof Blob !== 'undefined' && v instanceof Blob);","tryCatchPattern":"try { await separateVideoLayers({ src }); } catch (e) { if (e instanceof TypeError && e.message.includes('src must be a string, URL, or Blob')) { /* convert bytes to Blob or fix src */ } else throw e; }","preventionTips":["Convert raw buffers to Blob before passing","Pass el.src (string), not the element itself","Check src type in dev with an assert helper"],"tags":["type-mismatch","validation","src"],"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"}