{"record":{"id":"6fce86fe0b6068d8","repo":"remotion-dev/remotion","slug":"unexpected-error-content-length-is-null","errorCode":null,"errorMessage":"Unexpected error: content-length is null","messagePattern":"Unexpected error: content-length is null","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/studio/src/helpers/get-asset-metadata.ts","lineNumber":141,"sourceCode":"\t\tif (size === null) {\n\t\t\tconst file = await fetch(src, {\n\t\t\t\tmethod: 'HEAD',\n\t\t\t});\n\n\t\t\tif (file.status === 404) {\n\t\t\t\treturn {type: 'not-found'};\n\t\t\t}\n\n\t\t\tif (file.status !== 200) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Expected status code 200 or 404 for file, got ${file.status}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst contentLength = file.headers.get('content-length');\n\n\t\t\tif (!contentLength) {\n\t\t\t\tthrow new Error('Unexpected error: content-length is null');\n\t\t\t}\n\n\t\t\tsize = Number(contentLength);\n\t\t}\n\n\t\tconst fetchedAt = Date.now();\n\t\tconst srcWithTime = addTime ? addAssetCacheBust({fetchedAt, src}) : src;\n\n\t\tconst fileType = getPreviewFileType(\n\t\t\tcanvasContent.type === 'asset' ? canvasContent.asset : src,\n\t\t);\n\n\t\tif (fileType === 'video' || fileType === 'audio') {\n\t\t\tconst mediaMetadata = await getMediaMetadata(srcWithTime);\n\t\t\tif (mediaMetadata === null) {\n\t\t\t\tthrow new Error(`Could not read media metadata for ${src}`);\n\t\t\t}\n","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/studio/src/helpers/get-asset-metadata.ts#L123-L159","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","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.","Bypass intermediaries that strip the header (check with `curl -I` through the same path Studio uses) or reconfigure the proxy to forward content-length."],"exampleFix":"// before: dynamic/compressed route answers HEAD 200 without a length\napp.use(compression());\napp.get('/assets/:name', (req, res) => res.sendFile(dynamicPath));\n\n// after: uncompressed static serving sends Content-Length\napp.use('/assets', express.static(assetsDir)); // HEAD now includes content-length","handlingStrategy":"validation","validationCode":"const headHasLength = async (src: string): Promise<boolean> => {\n  const res = await fetch(src, {method: 'HEAD'});\n  if (res.status !== 200) return false;\n  const len = res.headers.get('content-length');\n  return len != null && len !== '';\n};\n\nif (!(await headHasLength(src))) {\n  // reconfigure the asset host to send Content-Length before relying on Studio metadata\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Serve assets from static file handlers that set `Content-Length`; disable compression/chunked encoding on asset routes.","Keep timeline assets out of dynamic/serverless routes that stream responses without a length.","Verify hosts with `curl -I` through the exact URL/proxy chain Studio uses.","Place files in the Studio project's `public/` folder when possible — the static-file listing carries the size and skips the HEAD path entirely."],"tags":["http","content-length","headers","asset-metadata","studio"],"backgroundTag":"missing-content-length-header","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-08-22T21:45:17.748Z","contentChangedAt":"2026-08-22T21:45:17.748Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}