remotion-dev/remotion · warning · Error

Unexpected error: content-length is null

Error message

Unexpected error: content-length is null

What it means

After a successful HEAD request (status 200), `getAssetMetadata()` reads the `content-length` header to determine asset size (packages/studio/src/helpers/get-asset-metadata.ts:80). If the header is absent or empty, it throws this error (caught and shown as `metadata-error`). It means the server answered 200 but did not declare a body length, which happens with chunked or compressed responses and proxies that strip the header.

Source

Thrown at packages/studio/src/helpers/get-asset-metadata.ts:141

		if (size === null) {
			const file = await fetch(src, {
				method: 'HEAD',
			});

			if (file.status === 404) {
				return {type: 'not-found'};
			}

			if (file.status !== 200) {
				throw new Error(
					`Expected status code 200 or 404 for file, got ${file.status}`,
				);
			}

			const contentLength = file.headers.get('content-length');

			if (!contentLength) {
				throw new Error('Unexpected error: content-length is null');
			}

			size = Number(contentLength);
		}

		const fetchedAt = Date.now();
		const srcWithTime = addTime ? addAssetCacheBust({fetchedAt, src}) : src;

		const fileType = getPreviewFileType(
			canvasContent.type === 'asset' ? canvasContent.asset : src,
		);

		if (fileType === 'video' || fileType === 'audio') {
			const mediaMetadata = await getMediaMetadata(srcWithTime);
			if (mediaMetadata === null) {
				throw new Error(`Could not read media metadata for ${src}`);
			}

View on GitHub (pinned to b2f4e34732)

Solutions

  1. Configure the server to include `Content-Length` on HEAD responses for assets — typically by disabling compression/chunking for that route or using static file serving that sets the header.
  2. Serve the file statically from the project `public/` folder instead of a dynamic route, so Studio reads the size from the static-file listing and never performs the HEAD request.
  3. Bypass intermediaries that strip the header (check with `curl -I` through the same path Studio uses) or reconfigure the proxy to forward content-length.

Example fix

// before: dynamic/compressed route answers HEAD 200 without a length
app.use(compression());
app.get('/assets/:name', (req, res) => res.sendFile(dynamicPath));

// after: uncompressed static serving sends Content-Length
app.use('/assets', express.static(assetsDir)); // HEAD now includes content-length
Defensive patterns

Strategy: validation

Validate before calling

const headHasLength = async (src: string): Promise<boolean> => {
  const res = await fetch(src, {method: 'HEAD'});
  if (res.status !== 200) return false;
  const len = res.headers.get('content-length');
  return len != null && len !== '';
};

if (!(await headHasLength(src))) {
  // reconfigure the asset host to send Content-Length before relying on Studio metadata
}

Prevention

When it happens

Trigger: A timeline asset/output URL whose HEAD response omits `Content-Length`: dynamically generated responses (chunked transfer-encoding), compression middleware that drops the length on HEAD, or intermediary proxies/HTTP2 servers that omit it. The 200 check at line 74 passes, then line 82 fires on the null header.

Common situations: Dev servers with compression enabled (Vite/Express `compression`) answering HEAD chunked; assets served from dynamic routes or serverless functions without an explicit length; reverse proxies (nginx defaults, some corporate proxies) removing content-length; assets served cross-origin through CDNs that stream responses.

Related errors


AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-08-22). Data as JSON: /api/errors/6fce86fe0b6068d8. Report an issue: GitHub.