{"record":{"id":"6a904c2c15fedc61","repo":"google/ExoPlayer","slug":"invalid-header-length","errorCode":null,"errorMessage":"Invalid header length","messagePattern":"Invalid header length","errorType":"exception","errorClass":"OpusDecoderException","httpStatus":null,"severity":"error","filePath":"extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java","lineNumber":113,"sourceCode":"    this.cryptoConfig = cryptoConfig;\n    if (cryptoConfig != null && !OpusLibrary.opusIsSecureDecodeSupported()) {\n      throw new OpusDecoderException(\"Opus decoder does not support secure decode\");\n    }\n    int initializationDataSize = initializationData.size();\n    if (initializationDataSize != 1 && initializationDataSize != 3) {\n      throw new OpusDecoderException(\"Invalid initialization data size\");\n    }\n    if (initializationDataSize == 3\n        && (initializationData.get(1).length != 8 || initializationData.get(2).length != 8)) {\n      throw new OpusDecoderException(\"Invalid pre-skip or seek pre-roll\");\n    }\n    preSkipSamples = getPreSkipSamples(initializationData);\n    seekPreRollSamples = getSeekPreRollSamples(initializationData);\n    skipSamples = preSkipSamples;\n\n    byte[] headerBytes = initializationData.get(0);\n    if (headerBytes.length < 19) {\n      throw new OpusDecoderException(\"Invalid header length\");\n    }\n    channelCount = getChannelCount(headerBytes);\n    if (channelCount > 8) {\n      throw new OpusDecoderException(\"Invalid channel count: \" + channelCount);\n    }\n    int gain = readSignedLittleEndian16(headerBytes, 16);\n\n    byte[] streamMap = new byte[8];\n    int numStreams;\n    int numCoupled;\n    if (headerBytes[18] == 0) { // Channel mapping\n      // If there is no channel mapping, use the defaults.\n      if (channelCount > 2) { // Maximum channel count with default layout.\n        throw new OpusDecoderException(\"Invalid header, missing stream map\");\n      }\n      numStreams = 1;\n      numCoupled = (channelCount == 2) ? 1 : 0;\n      streamMap[0] = 0;","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/OpusDecoder.java#L95-L131","documentation":"Thrown by the Opus extension decoder when the first initialization data entry (the Opus identification header copied from the container) is shorter than 19 bytes. The constructor needs at least 19 bytes to read channel count (offset 9), pre-skip/gain (offset 16) and the channel mapping family byte (offset 18). A truncated header means the extractor or upstream metadata producer did not supply a complete Opus ID header.","triggerScenarios":"Constructing OpusDecoder (via DefaultRenderersFactory building an OpusRenderer, or directly) where initializationData.get(0).length < 19. Happens when an extractor emits a partial Opus header, when initializationData is built by hand, or when a non-Opus track is misrouted to the Opus decoder.","commonSituations":"Playing WebM/MKV or HLS with Opus audio where the container's CodecPrivate is truncated; constructing the format's initializationData manually from a wrong offset; version mismatches between extractor and extension that change what initializationData[0] contains.","solutions":["Log initializationData sizes before decoder construction; entry 0 must be >= 19 bytes (full Opus ID header 'OpusHead' + version + channels + pre-skip + sample rate + gain + mapping family).","If you build initializationData yourself, pass the complete Opus identification header starting with the 'OpusHead' magic, not just the first bytes of CodecPrivate.","Verify the sample MIME type is audio/opus (MimeTypes.AUDIO_OPUS) so only real Opus tracks reach this decoder.","If the stream is genuinely malformed, reject the track at selection time rather than crashing the renderer."],"exampleFix":"// before\nList<byte[]> initData = format.initializationData; // entry 0 is only 8 bytes\n\n// after\n// Validate the ID header before handing the format to the Opus renderer\nbyte[] header = format.initializationData.get(0);\nif (header.length < 19\n    || header[0] != 'O' || header[1] != 'p' || header[2] != 'u' || header[3] != 's') {\n  throw new IllegalArgumentException(\"Track has no complete Opus identification header\");\n}","handlingStrategy":"validation","validationCode":"byte[] h = format.initializationData.get(0);\nboolean ok = h != null && h.length >= 19 && h[0]=='O' && h[1]=='p' && h[2]=='u' && h[3]=='s' && h[4]=='H' && h[5]=='e' && h[6]=='a' && h[7]=='d';\nif (!ok) { /* deselect track / fail before renderer init */ }","typeGuard":null,"tryCatchPattern":"catch (OpusDecoderException e) when constructing the renderer only to log; prefer pre-validating the Format so the decoder is never built with a short header.","preventionTips":["Always verify 'OpusHead' magic and length >= 19 before enabling the Opus extension for a track.","Never hand-assemble initializationData; pass through what the extractor produced.","Confirm MimeTypes.AUDIO_OPUS routing so foreign headers never reach OpusDecoder."],"tags":["opus","audio","decoder","exoplayer-extension","initialization-data"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}