{"record":{"id":"67a2e9da537e181c","repo":"remotion-dev/remotion","slug":"expected-m3u-text-value","errorCode":null,"errorMessage":"Expected m3u-text-value","messagePattern":"Expected m3u-text-value","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/m3u/get-chunks.ts","lineNumber":22,"sourceCode":"\tduration: number;\n\turl: string;\n\tisHeader: boolean;\n};\n\nexport const getChunks = (playlist: M3uPlaylist) => {\n\tconst chunks: M3uChunk[] = [];\n\tfor (let i = 0; i < playlist.boxes.length; i++) {\n\t\tconst box = playlist.boxes[i];\n\t\tif (box.type === 'm3u-map') {\n\t\t\tchunks.push({duration: 0, url: box.value, isHeader: true});\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (box.type === 'm3u-extinf') {\n\t\t\tconst nextBox = playlist.boxes[i + 1];\n\t\t\ti++;\n\t\t\tif (nextBox.type !== 'm3u-text-value') {\n\t\t\t\tthrow new Error('Expected m3u-text-value');\n\t\t\t}\n\n\t\t\tchunks.push({duration: box.value, url: nextBox.value, isHeader: false});\n\t\t}\n\n\t\tcontinue;\n\t}\n\n\treturn chunks;\n};\n","sourceCodeStart":4,"sourceCodeEnd":33,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/m3u/get-chunks.ts#L4-L33","documentation":"Thrown by getChunks() while iterating a media playlist's boxes. Per RFC 8216 every #EXTINF directive must be immediately followed by a media segment URI line. The parser stores EXTINF as an m3u-extinf box and the URI as an m3u-text-value box. At get-chunks.ts:21 it asserts that the box following m3u-extinf is of type m3u-text-value; if it is not (missing, wrong type, or playlist ended), parsing aborts.","triggerScenarios":"A media playlist where #EXTINF is the last line with no segment URI following it; a directive or comment line appearing between #EXTINF and its segment URL; or a playlist truncated mid-way so the text-value box was never emitted by the line parser.","commonSituations":"Truncated HTTP response when fetching the media playlist (network timeout, proxy cutting the connection); hand-edited m3u8 files with misplaced lines; live playlists served incomplete during a rolling-window boundary.","solutions":["Verify the media playlist is complete — every #EXTINF line must have a segment URI on the next line","Retry the fetch to rule out transient network truncation of the playlist response","Validate the playlist with an HLS conformance tool (e.g. hlsverify, mediabunny) before parsing"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-fetch the media playlist text and verify every EXTINF is followed by a URI\nconst text = await (await fetch(playlistUrl)).text();\nconst lines = text.split('\\n');\nfor (let i = 0; i < lines.length; i++) {\n  if (lines[i].startsWith('#EXTINF')) {\n    if (i + 1 >= lines.length || !lines[i + 1].trim() || lines[i + 1].startsWith('#')) {\n      throw new Error('Playlist has EXTINF without a following segment URI');\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await parseMedia({src: mediaPlaylistUrl});\n} catch (e) {\n  if (e instanceof Error && e.message === 'Expected m3u-text-value') {\n    // the media playlist is malformed or truncated — retry or reject\n  }\n  throw e;\n}","preventionTips":["Wrap parseMedia in try-catch when parsing HLS streams from untrusted or unstable sources","Retry on truncation errors since they are often transient network issues"],"tags":["hls","m3u8","truncated","playlist-structure","extinf"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}