{"record":{"id":"edfa343979fa3b15","repo":"remotion-dev/remotion","slug":"expected-aac-codec-private-data","errorCode":null,"errorMessage":"Expected AAC codec private data","messagePattern":"Expected AAC codec private data","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/iso-base-media/get-actual-number-of-channels.ts","lineNumber":41,"sourceCode":"\taudioCodec: MediaParserAudioCodec;\n\tcodecPrivate: MediaParserCodecData | null;\n\tnumberOfChannels: number;\n\tsampleRate: number;\n}): AudioDecoderConfig => {\n\tif (audioCodec !== 'aac') {\n\t\treturn {\n\t\t\tnumberOfChannels,\n\t\t\tsampleRate,\n\t\t\tcodecPrivate,\n\t\t};\n\t}\n\n\tif (codecPrivate === null) {\n\t\treturn {numberOfChannels, sampleRate, codecPrivate};\n\t}\n\n\tif (codecPrivate.type !== 'aac-config') {\n\t\tthrow new Error('Expected AAC codec private data');\n\t}\n\n\tconst parsed = parseAacCodecPrivate(codecPrivate.data);\n\n\tconst actual = createAacCodecPrivate({\n\t\t...parsed,\n\t\tcodecPrivate: codecPrivate.data,\n\t});\n\n\treturn {\n\t\tnumberOfChannels: parsed.channelConfiguration,\n\t\tsampleRate: parsed.sampleRate,\n\t\tcodecPrivate: {type: 'aac-config', data: actual},\n\t};\n};\n","sourceCodeStart":23,"sourceCodeEnd":57,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/iso-base-media/get-actual-number-of-channels.ts#L23-L57","documentation":"Thrown by getActualDecoderParameters when an audio track is labeled codec 'aac' and codecPrivate is non-null, but codecPrivate.type is not 'aac-config'. The function only knows how to derive real channel count and sample rate from a properly shaped AAC-specific config blob; any other shape is a logic/data mismatch rather than a missing field. It is an internal contract violation: the upstream codecPrivate producer mislabeled the payload.","triggerScenarios":"Reachable when getCodecPrivateFromTrak or the esds parser yields a codecPrivate object whose type is something like 'av1-config' or a raw buffer wrapper, while getAudioCodecFromTrack separately classified the track as AAC. Most often a parser bug or a malformed esds box where the descriptor type does not match the audio codec family.","commonSituations":"MP4 files with non-standard or corrupted esds (Elementary Stream Descriptor) boxes. Files where the audio object type is ambiguous and one code path labels it AAC while another attaches non-AAC codec private data. Mismatch after a partial media-parser upgrade that changed the codecPrivate discriminated union.","solutions":["Re-mux the audio track with ffmpeg: `ffmpeg -i in.mp4 -c:a aac -b:a 128k out.mp4` to regenerate a clean esds.","If you control the caller, narrow audioCodec before invoking getActualDecoderParameters: only pass AAC private data when codecPrivate.type === 'aac-config'.","File a media-parser issue with the offending file; this typically indicates a parser regression.","Wrap the call in try/catch and fall back to the original numberOfChannels/sampleRate when private-data parsing fails."],"exampleFix":"// before\nconst actual = getActualDecoderParameters({ audioCodec, codecPrivate, numberOfChannels, sampleRate });\n\n// after\nconst actual =\n  audioCodec === 'aac' && codecPrivate && codecPrivate.type !== 'aac-config'\n    ? { numberOfChannels, sampleRate, codecPrivate: null } // fall back: ignore mismatched private data\n    : getActualDecoderParameters({ audioCodec, codecPrivate, numberOfChannels, sampleRate });","handlingStrategy":"type-guard","validationCode":"import type {MediaParserCodecData} from '../../codec-data';\nfunction isAacConfigPrivate(data: MediaParserCodecData | null): data is { type: 'aac-config'; data: Uint8Array } {\n  return data !== null && data.type === 'aac-config';\n}","typeGuard":"import type {MediaParserCodecData} from '../../codec-data';\nfunction isAacCodecPrivate(data: MediaParserCodecData | null): boolean {\n  return data === null || data.type === 'aac-config';\n}\n// Use before getActualDecoderParameters:\nif (audioCodec === 'aac' && !isAacCodecPrivate(codecPrivate)) {\n  codecPrivate = null; // discard mismatched private data\n}","tryCatchPattern":"try {\n  const actual = getActualDecoderParameters({ audioCodec, codecPrivate, numberOfChannels, sampleRate });\n} catch (err) {\n  if (/Expected AAC codec private data/i.test(String(err?.message))) {\n    // Fall back to declared channel count; ignore the bad private data\n    return { numberOfChannels, sampleRate, codecPrivate: null };\n  }\n  throw err;\n}","preventionTips":["Narrow codecPrivate by type before passing it into AAC-specific helpers.","If you produce codecPrivate objects, always set type to 'aac-config' for AAC.","Wrap audio decoder parameter resolution in a fallback that drops codecPrivate on mismatch.","Re-mux audio tracks with a recent ffmpeg AAC encoder to regenerate a clean esds."],"tags":["mp4","isobmff","media-parser","aac","audio","codec-private"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}