{"record":{"id":"44d71b56d7c0b1d4","repo":"remotion-dev/remotion","slug":"expected-src-to-be-a-string","errorCode":null,"errorMessage":"Expected src to be a string","messagePattern":"Expected src to be a string","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/m3u/parse-m3u.ts","lineNumber":26,"sourceCode":"\tif (state.m3u.isReadyToIterateOverM3u()) {\n\t\tconst selectedPlaylists = state.m3u.getSelectedPlaylists();\n\n\t\tconst whichPlaylistToRunOver =\n\t\t\tstate.m3u.sampleSorter.getNextStreamToRun(selectedPlaylists);\n\n\t\tawait runOverM3u({\n\t\t\tstate,\n\t\t\tstructure,\n\t\t\tplaylistUrl: whichPlaylistToRunOver,\n\t\t\tlogLevel: state.logLevel,\n\t\t});\n\n\t\treturn null;\n\t}\n\n\tif (state.m3u.hasFinishedManifest()) {\n\t\tif (typeof state.src !== 'string' && !(state.src instanceof URL)) {\n\t\t\tthrow new Error('Expected src to be a string');\n\t\t}\n\n\t\tstate.mediaSection.addMediaSection({\n\t\t\tstart: 0,\n\t\t\t// We do a pseudo-seek when seeking m3u, which will be the same byte\n\t\t\t// as we are currently in, which in most cases is the end of the file.\n\t\t\tsize: state.contentLength + 1,\n\t\t});\n\n\t\tawait afterManifestFetch({\n\t\t\tstructure,\n\t\t\tm3uState: state.m3u,\n\t\t\tsrc: state.src.toString(),\n\t\t\tselectM3uStreamFn: state.selectM3uStreamFn,\n\t\t\tlogLevel: state.logLevel,\n\t\t\tselectAssociatedPlaylistsFn: state.selectM3uAssociatedPlaylistsFn,\n\t\t\treaderInterface: state.readerInterface,\n\t\t\tonAudioTrack: state.onAudioTrack,","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/m3u/parse-m3u.ts#L8-L44","documentation":"Thrown when an HLS/M3U8 manifest has been fully parsed but the parser's internal `state.src` is neither a `string` nor a `URL` object. The parser needs a resolvable URL to fetch subsequent playlist segments, and a non-URL src (like a `File`, `Blob`, `ArrayBuffer`, or `Uint8Array`) makes segment resolution impossible.","triggerScenarios":"Calling `parseMedia()` (or `downloadAndParseMedia()`) with an `m3u8`/HLS stream where the `src` option is a `File`, `Blob`, `ArrayBuffer`, `Uint8Array`, `ReadableStream`, or any non-string/non-URL value, and the manifest finishes loading so the parser reaches the `hasFinishedManifest()` branch in parse-m3u.ts:24-27.","commonSituations":"Passing a locally-read `.m3u8` file blob or `ArrayBuffer` fetched via `fetch().arrayBuffer()` directly into `parseMedia({src})` instead of passing the remote URL string. Trying to parse a downloaded master playlist that references relative segment URLs without a base URL.","solutions":["Pass the HLS playlist as a URL string (e.g., `parseMedia({src: 'https://example.com/stream.m3u8'})`) so the parser can resolve and fetch segments.","If you have the manifest text but no URL, host it so it has a resolvable base URL and pass that URL as `src`.","If you must parse from a buffer, convert MP4/WebM files directly instead of wrapping them in an M3U8 container.","Verify `typeof src === 'string' || src instanceof URL` before calling `parseMedia` for HLS sources."],"exampleFix":"// before\nconst res = await fetch('https://example.com/stream.m3u8');\nconst buf = await res.arrayBuffer();\nawait parseMedia({src: buf}); // throws for m3u8\n\n// after\nawait parseMedia({src: 'https://example.com/stream.m3u8'});","handlingStrategy":"validation","validationCode":"// Before calling parseMedia with an HLS source\nimport {parseMedia} from '@remotion/media-parser';\n\nconst src = options.src;\nif (src instanceof URL || typeof src === 'string') {\n  // Safe for M3U8 - has a resolvable URL\n  await parseMedia({src, fields: {durationInSeconds: true}});\n} else {\n  throw new Error(\n    'HLS/M3U8 sources require a string or URL src. ' +\n    'Buffers, Blobs, and Files are not supported for M3U8.'\n  );\n}","typeGuard":"// Type guard for M3U-compatible src values\nfunction isUrlSrc(src: unknown): src is string | URL {\n  return typeof src === 'string' || src instanceof URL;\n}","tryCatchPattern":"try {\n  await parseMedia({src, fields: {durationInSeconds: true}});\n} catch (e) {\n  if (e instanceof Error && e.message === 'Expected src to be a string') {\n    console.error('HLS sources require a URL string src, not a buffer or file.');\n  }\n  throw e;\n}","preventionTips":["Always pass HLS/M3U8 sources as URL strings to parseMedia.","Do not pass ArrayBuffer, Blob, or File objects as src for M3U8 playlists.","For non-URL MP4/WebM sources, use buffer-based parsing instead of HLS."],"tags":["hls","m3u8","src-type","url-resolution"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}