remotion-dev/remotion · error · Error
Cannot read media ${src} without a content length. This is c
Error message
Cannot read media ${src} without a content length. This is currently not supported. Ensure the media has a "Content-Length" HTTP header. What it means
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.
Source
Thrown at packages/media-parser/src/internal-parse-media.ts:81
const prefetchCache = new Map<string, ReturnType<typeof makeFetchRequest>>();
const {
reader: readerInstance,
contentLength,
name,
contentType,
supportsContentRange,
needsContentRange,
} = await readerInterface.read({
src,
range: null,
controller,
logLevel,
prefetchCache,
});
if (contentLength === null) {
throw new Error(
`Cannot read media ${src} without a content length. This is currently not supported. Ensure the media has a "Content-Length" HTTP header.`,
);
}
if (!supportsContentRange && needsContentRange) {
throw new Error(
'Cannot read media without it supporting the "Content-Range" header. This is currently not supported. Ensure the media supports the "Content-Range" HTTP header.',
);
}
const hasAudioTrackHandlers = Boolean(onAudioTrack);
const hasVideoTrackHandlers = Boolean(onVideoTrack);
const state = makeParserState({
hasAudioTrackHandlers,
hasVideoTrackHandlers,
controller,
onAudioTrack: onAudioTrack ?? null,View on GitHub (pinned to 78fe4bb3fd)
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.
Example fix
// before: server omits Content-Length
app.get('/video', (req, res) => fs.createReadStream(path).pipe(res));
// after: send Content-Length and support ranges
const stat = fs.statSync(path);
res.status(200)
.set('Content-Length', String(stat.size))
.set('Accept-Ranges', 'bytes')
.set('Content-Type', 'video/mp4');
fs.createReadStream(path).pipe(res); Defensive patterns
Strategy: validation
Validate before calling
// Probe headers before parsing to confirm Content-Length is present
const head = await fetch(url, {method: 'GET', headers: {Range: 'bytes=0-0'}});
if (!head.headers.get('content-length')) {
throw new Error('Server omits Content-Length; cannot parse via fetchReader');
} Try / catch
try {
await parseMedia({src: url, fields: {durationInSeconds: true}});
} catch (e) {
if (e instanceof Error && e.message.includes('without a content length')) {
// switch to a nodeReader-backed parse or fix the origin
throw new Error('Origin must send Content-Length for media');
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot read media without it supporting the "Content-Range"
- Range header (${requestedRange}) does not match content-rang
- Server returned status code ${res.status} for ${resolvedUrl}
- Read past end of file
- Unexpected end of file
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/924f83be0bd662fb.
Report an issue: GitHub.