{"record":{"id":"ef10203634b553fc","repo":"remotion-dev/remotion","slug":"stream-does-not-have-a-resolution","errorCode":null,"errorMessage":"Stream does not have a resolution","messagePattern":"Stream does not have a resolution","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/m3u/after-manifest-fetch.ts","lineNumber":60,"sourceCode":"\t// 2. If streams === null: Single media playlist (has EXT-X-INDEPENDENT-SEGMENTS but no EXT-X-STREAM-INF)\n\t// Both cases should iterate over the current URL as the media playlist\n\tif (!independentSegments || streams === null) {\n\t\tif (!src) {\n\t\t\tthrow new Error('No src');\n\t\t}\n\n\t\tm3uState.setSelectedMainPlaylist({\n\t\t\ttype: 'initial-url',\n\t\t\turl: src,\n\t\t});\n\n\t\treturn m3uState.setReadyToIterateOverM3u();\n\t}\n\n\tconst selectedPlaylist = await selectStream({streams, fn: selectM3uStreamFn});\n\n\tif (!selectedPlaylist.dimensions) {\n\t\tthrow new Error('Stream does not have a resolution');\n\t}\n\n\tm3uState.setSelectedMainPlaylist({\n\t\ttype: 'selected-stream',\n\t\tstream: selectedPlaylist,\n\t});\n\n\tconst skipAudioTracks =\n\t\tonAudioTrack === null && canSkipTracks.doFieldsNeedTracks() === false;\n\n\tconst associatedPlaylists = await selectAssociatedPlaylists({\n\t\tplaylists: selectedPlaylist.associatedPlaylists,\n\t\tfn: selectAssociatedPlaylistsFn,\n\t\tskipAudioTracks,\n\t});\n\tm3uState.setAssociatedPlaylists(associatedPlaylists);\n\n\tconst playlistUrls = [","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/m3u/after-manifest-fetch.ts#L42-L78","documentation":"Thrown by afterManifestFetch after a stream variant is selected from an HLS master playlist via selectStream(). The selected variant's #EXT-X-STREAM-INF line must include a RESOLUTION attribute (e.g. RESOLUTION=1920x1080). The parser stores this as M3uStream.dimensions and requires it to be non-null at after-manifest-fetch.ts:59 because downstream decoding needs a concrete video frame size to proceed.","triggerScenarios":"A master playlist where the #EXT-X-STREAM-INF line for the selected variant omits RESOLUTION (common for audio-only variants or streams from encoders that don't set it). Also fired when a custom selectM3uStream callback returns the id of a variant whose dimensions are null — for example selecting an audio-only stream or an I-frame-only variant.","commonSituations":"Audio-only HLS streams that have no video resolution; playlists from CDNs or encoders (FFmpeg, MediaConvert) that omit RESOLUTION on some variants; a user-provided selectM3uStream callback that picks streams[0] without checking whether dimensions exist.","solutions":["Provide a custom selectM3uStream callback that filters to streams where stream.dimensions !== null before returning an id","Ensure the master playlist #EXT-X-STREAM-INF lines include RESOLUTION=WxH for every video variant","If the stream is genuinely audio-only, parse the audio-only media playlist directly instead of through the master-playlist path"],"exampleFix":"// before — default callback may pick a resolution-less variant\nawait parseMedia({src: 'https://cdn.example.com/master.m3u8'});\n\n// after — select a variant that has dimensions\nawait parseMedia({\n  src: 'https://cdn.example.com/master.m3u8',\n  selectM3uStream: ({streams}) => {\n    const withRes = streams.filter((s) => s.dimensions !== null);\n    if (withRes.length === 0) throw new Error('No video variants');\n    return withRes[0].id;\n  },\n});","handlingStrategy":"validation","validationCode":"// Pass a custom selectM3uStream that skips resolution-less variants\nawait parseMedia({\n  src: 'https://cdn.example.com/master.m3u8',\n  selectM3uStream: ({streams}) => {\n    const video = streams.filter((s) => s.dimensions !== null);\n    if (video.length === 0) {\n      throw new Error('No video variant with resolution found');\n    }\n    return video[0].id;\n  },\n});","typeGuard":"// Type guard for streams that carry resolution\nconst hasResolution = (s: M3uStream): s is M3uStream & {dimensions: {width: number; height: number}} =>\n  s.dimensions !== null;","tryCatchPattern":"try {\n  await parseMedia({src, selectM3uStream: myFn});\n} catch (e) {\n  if (e instanceof Error && e.message === 'Stream does not have a resolution') {\n    // handle: pick a different stream or fall back\n  }\n  throw e;\n}","preventionTips":["Always provide a custom selectM3uStream callback that filters on dimensions !== null when parsing master playlists with audio-only variants","Inspect available streams via the onM3uStreams callback before selection to understand which variants have resolution"],"tags":["hls","m3u8","resolution","stream-selection","master-playlist"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}