{"record":{"id":"321ffc4b3ea2fc88","repo":"remotion-dev/remotion","slug":"read-past-end-of-file","errorCode":null,"errorMessage":"Read past end of file","messagePattern":"Read past end of file","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/iso-base-media/get-moov-atom.ts","lineNumber":124,"sourceCode":"\t\t\tlogLevel: state.logLevel,\n\t\t\tonlyIfMoovAtomExpected: {\n\t\t\t\ttracks: tracksState,\n\t\t\t\tisoState: null,\n\t\t\t\tmovieTimeScaleState: state.iso.movieTimeScale,\n\t\t\t\tonAudioTrack,\n\t\t\t\tonVideoTrack,\n\t\t\t\tregisterVideoSampleCallback: () => Promise.resolve(),\n\t\t\t\tregisterAudioSampleCallback: () => Promise.resolve(),\n\t\t\t},\n\t\t\tonlyIfMdatAtomExpected: null,\n\t\t\tcontentLength: state.contentLength - endOfMdat,\n\t\t});\n\t\tif (box.type === 'box') {\n\t\t\tboxes.push(box.box);\n\t\t}\n\n\t\tif (iterator.counter.getOffset() + endOfMdat > state.contentLength) {\n\t\t\tthrow new Error('Read past end of file');\n\t\t}\n\n\t\tif (iterator.counter.getOffset() + endOfMdat === state.contentLength) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tconst moov = boxes.find((b) => b.type === 'moov-box');\n\tif (!moov) {\n\t\tthrow new Error('No moov box found');\n\t}\n\n\tLog.verbose(\n\t\tstate.logLevel,\n\t\t`Finished fetching moov atom in ${Date.now() - start}ms`,\n\t);\n\n\treturn moov;","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/iso-base-media/get-moov-atom.ts#L106-L142","documentation":"Thrown during the second fetch performed by getMoovAtom (when moov sits at the end of the file and the parser must range-request the tail) if iterator.counter.getOffset() + endOfMdat exceeds state.contentLength. This means the parser consumed more bytes than the file is supposed to contain — typically because contentLength was misreported or the actual response body is larger/structured differently than declared.","triggerScenarios":"Reached after moov is not at the head and the parser re-fetches from endOfMdat onward. If the server returns more bytes than the original Content-Length (e.g. the source grew between requests, a proxy injected content, or contentLength was misreported by the caller) the offset will overshoot. Also reachable if endOfMdat is computed wrong, double-counting bytes.","commonSituations":"Live or append-only MP4s that grow between the HEAD and the range request. Misreported contentLength by a custom readerInterface. Proxies that decompress or transform the body. Range request that ignores Range headers and returns the whole file.","solutions":["Ensure the readerInterface honors the requested byte range and the source reports a stable Content-Length.","Pre-fetch the file size with a HEAD request and pass an accurate contentLength to parseMedia.","If the source is a growing file, snapshot it to a stable URL before parsing.","Implement a readerInterface that clamps reads to contentLength to avoid overshoot."],"exampleFix":"// before\nawait parseMedia({ src: growingUrl, readerInterface: customReader });\n\n// after\n// Snapshot the current size and use a byte-serving endpoint\nconst head = await fetch(growingUrl, { method: 'HEAD' });\nconst size = Number(head.headers.get('content-length'));\nawait parseMedia({\n  src: `${growingUrl}?snapshot=${size}`,\n  readerInterface: rangeServingReader,\n});","handlingStrategy":"validation","validationCode":"// Ensure contentLength is accurate and the reader honors ranges\nasync function verifyStableSize(url: string): Promise<number> {\n  const h1 = await fetch(url, { method: 'HEAD' });\n  const size1 = Number(h1.headers.get('content-length'));\n  const h2 = await fetch(url, { method: 'HEAD' });\n  const size2 = Number(h2.headers.get('content-length'));\n  if (!Number.isFinite(size1) || size1 !== size2) throw new Error('Source size is unstable; snapshot before parsing');\n  return size1;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await parseMedia({ src, fields: { duration: true } });\n} catch (err) {\n  if (/Read past end of file/i.test(String(err?.message))) {\n    throw new Error('Source reported an inconsistent size. Snapshot the file or fix Content-Length and retry.');\n  }\n  throw err;\n}","preventionTips":["Pre-flight a HEAD request and pass an accurate contentLength to parseMedia.","Use a byte-serving endpoint that honors Range headers strictly.","Snapshot growing files to a stable URL/size before parsing.","Ensure your custom readerInterface clamps each read to the declared length."],"tags":["mp4","isobmff","media-parser","range-request","streaming","content-length"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}