{"record":{"id":"6f31c61849c23744","repo":"google/ExoPlayer","slug":"failed-to-decode-streaminfo","errorCode":null,"errorMessage":"Failed to decode StreamInfo","messagePattern":"Failed to decode StreamInfo","errorType":"exception","errorClass":"FlacDecoderException","httpStatus":null,"severity":"error","filePath":"extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacDecoder.java","lineNumber":75,"sourceCode":"   *     which is the flac file header.\n   * @throws FlacDecoderException Thrown if an exception occurs when initializing the decoder.\n   */\n  public FlacDecoder(\n      int numInputBuffers,\n      int numOutputBuffers,\n      int maxInputBufferSize,\n      List<byte[]> initializationData)\n      throws FlacDecoderException {\n    super(new DecoderInputBuffer[numInputBuffers], new SimpleDecoderOutputBuffer[numOutputBuffers]);\n    if (initializationData.size() != 1) {\n      throw new FlacDecoderException(\"Initialization data must be of length 1\");\n    }\n    decoderJni = new FlacDecoderJni();\n    decoderJni.setData(ByteBuffer.wrap(initializationData.get(0)));\n    try {\n      streamMetadata = decoderJni.decodeStreamMetadata();\n    } catch (ParserException e) {\n      throw new FlacDecoderException(\"Failed to decode StreamInfo\", e);\n    } catch (IOException e) {\n      // Never happens.\n      throw new IllegalStateException(e);\n    }\n\n    int initialInputBufferSize =\n        maxInputBufferSize != Format.NO_VALUE ? maxInputBufferSize : streamMetadata.maxFrameSize;\n    setInitialInputBufferSize(initialInputBufferSize);\n  }\n\n  @Override\n  public String getName() {\n    return \"libflac\";\n  }\n\n  @Override\n  protected DecoderInputBuffer createInputBuffer() {\n    return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/google/ExoPlayer/blob/dd430f7053a1a3958deea3ead6a0565150c06bfc/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacDecoder.java#L57-L93","documentation":"FlacDecoderException wrapping a ParserException thrown when decoderJni.decodeStreamMetadata() cannot parse the bytes set from initializationData[0] as a FLAC STREAMINFO block. Unlike the length check, this means one entry existed but its contents are not a valid stream-info header (wrong bytes, wrong offset, or corrupt header).","triggerScenarios":"initializationData[0] contains bytes that are not the 34-byte STREAMINFO block (e.g. an ID3 tag remnant, the whole file header including the magic, or a partial read); truncated FLAC file; header bytes byte-swapped or re-encoded by an upstream transform.","commonSituations":"Corrupt downloads; FLAC files with non-standard prepended metadata; custom extractors passing the wrong slice of the header; files that fail sniffing and get misparsed as FLAC.","solutions":["Verify initializationData[0] is exactly the STREAMINFO block: expect 34 bytes starting after the 'fLaC' magic with block header 0x00 (last-block=0, type=0)","Re-download or re-mux the source file; validate it with 'flac -t' or ffprobe outside the app","If writing a custom extractor, use FlacConstants and the library's own FrameReader/metadata parsing rather than slicing manually","Catch FlacDecoderException at renderer construction and surface a 'corrupt file' message or fall back to platform decoding"],"exampleFix":"// before: passing the whole file header (magic + block header included)\nList<byte[]> init = Collections.singletonList(first38Bytes);\nnew FlacDecoder(numIn, numOut, maxIn, init); // ParserException: bad StreamInfo\n\n// after: pass only the 34-byte STREAMINFO payload\nList<byte[]> init = Collections.singletonList(\n    Arrays.copyOfRange(header, 4 /* magic */ + 4 /* block header */, 4 + 4 + 34));\nnew FlacDecoder(numIn, numOut, maxIn, init);","handlingStrategy":"try-catch","validationCode":"boolean looksLikeStreamInfo(byte[] block) {\n  return block != null && block.length == 34\n      && block[0] != 0; // sampleRate bits (top 20 bits) must be non-zero for real STREAMINFO\n}\n\nif (!looksLikeStreamInfo(format.initializationData.get(0))) {\n  // header bytes are wrong/corrupt: fail with a clear message before decoder init\n}","typeGuard":null,"tryCatchPattern":"try {\n  decoder = new FlacDecoder(numIn, numOut, maxIn, format.initializationData);\n} catch (FlacDecoderException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"StreamInfo\")) {\n    // header block invalid: mark source as corrupt and skip / re-download\n    markSourceCorrupt(uri);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Pass exactly the 34-byte STREAMINFO payload — never the 'fLaC' magic or the 4-byte block header","Validate downloaded FLAC files (ffprobe / flac -t) in your ingest pipeline","Surface ParserException-based failures as 'corrupt file' to users instead of generic decode errors"],"tags":["android","flac","metadata","parser","corrupt-file"],"backgroundTag":null,"analyzedSha":"dd430f7053a1a3958deea3ead6a0565150c06bfc","analyzedAt":"2026-08-14T12:22:02.982Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}