{"record":{"id":"924f83be0bd662fb","repo":"remotion-dev/remotion","slug":"cannot-read-media-src-without-a-content-length","errorCode":null,"errorMessage":"Cannot read media ${src} without a content length. This is currently not supported. Ensure the media has a \"Content-Length\" HTTP header.","messagePattern":"Cannot read media (.+?) without a content length\\. This is currently not supported\\. Ensure the media has a \"Content-Length\" HTTP header\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/internal-parse-media.ts","lineNumber":81,"sourceCode":"\tconst prefetchCache = new Map<string, ReturnType<typeof makeFetchRequest>>();\n\n\tconst {\n\t\treader: readerInstance,\n\t\tcontentLength,\n\t\tname,\n\t\tcontentType,\n\t\tsupportsContentRange,\n\t\tneedsContentRange,\n\t} = await readerInterface.read({\n\t\tsrc,\n\t\trange: null,\n\t\tcontroller,\n\t\tlogLevel,\n\t\tprefetchCache,\n\t});\n\n\tif (contentLength === null) {\n\t\tthrow new Error(\n\t\t\t`Cannot read media ${src} without a content length. This is currently not supported. Ensure the media has a \"Content-Length\" HTTP header.`,\n\t\t);\n\t}\n\n\tif (!supportsContentRange && needsContentRange) {\n\t\tthrow new Error(\n\t\t\t'Cannot read media without it supporting the \"Content-Range\" header. This is currently not supported. Ensure the media supports the \"Content-Range\" HTTP header.',\n\t\t);\n\t}\n\n\tconst hasAudioTrackHandlers = Boolean(onAudioTrack);\n\tconst hasVideoTrackHandlers = Boolean(onVideoTrack);\n\n\tconst state = makeParserState({\n\t\thasAudioTrackHandlers,\n\t\thasVideoTrackHandlers,\n\t\tcontroller,\n\t\tonAudioTrack: onAudioTrack ?? null,","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/internal-parse-media.ts#L63-L99","documentation":"After the initial readerInterface.read, internalParseMedia checks contentLength. The fetch reader derives contentLength from the Content-Length HTTP header; if it is null the parser throws because seek/range logic, progress reporting, and the infinite-loop guard all depend on a known total length. HLS (.m3u8) and .ts endpoints can live without it, but ordinary HTTP media cannot.","triggerScenarios":"Fetching media over HTTP where the server omits Content-Length (chunked transfer-encoding, compressed on the fly, or a proxy that strips the header). Reached on the very first read in internalParseMedia when contentLength comes back null for a non-.m3u8/.ts resource.","commonSituations":"CDN/proxy (Cloudflare, nginx with gzip/brotli dynamic compression, Server-Sent-Events endpoints) stripping Content-Length; serving from an S3 presigned URL with chunked encoding; legacy streaming servers; localhost dev servers using chunked responses.","solutions":["Configure the origin/CDN to send Content-Length for media assets (disable dynamic compression for media MIME types, or serve pre-compressed/static files).","Serve files statically (Content-Length is set automatically for static file responses) instead of through a streaming script.","If you control the server, set Content-Length explicitly when streaming a known-size file.","For Node, use the nodeFileSystemReader (reader: nodeReader) which reads the file size from the FS instead of HTTP."],"exampleFix":"// before: server omits Content-Length\napp.get('/video', (req, res) => fs.createReadStream(path).pipe(res));\n\n// after: send Content-Length and support ranges\nconst stat = fs.statSync(path);\nres.status(200)\n  .set('Content-Length', String(stat.size))\n  .set('Accept-Ranges', 'bytes')\n  .set('Content-Type', 'video/mp4');\nfs.createReadStream(path).pipe(res);","handlingStrategy":"validation","validationCode":"// Probe headers before parsing to confirm Content-Length is present\nconst head = await fetch(url, {method: 'GET', headers: {Range: 'bytes=0-0'}});\nif (!head.headers.get('content-length')) {\n  throw new Error('Server omits Content-Length; cannot parse via fetchReader');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await parseMedia({src: url, fields: {durationInSeconds: true}});\n} catch (e) {\n  if (e instanceof Error && e.message.includes('without a content length')) {\n    // switch to a nodeReader-backed parse or fix the origin\n    throw new Error('Origin must send Content-Length for media');\n  }\n  throw e;\n}","preventionTips":["Serve media statically so the host sets Content-Length automatically.","Disable dynamic compression (gzip/brotli) for media MIME types.","Use nodeReader (reader: nodeReader) for local files in Node to bypass HTTP length.","Verify with curl -I that Content-Length is present before integrating."],"tags":["network","http-headers","content-length","cdn","media-parser"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}