google/ExoPlayer · error · IOException

Cannot read frame at position ${lastDecodePosition}

Error message

Cannot read frame at position ${lastDecodePosition}

What it means

Wraps a FlacDecoderJni.FlacFrameDecodeException raised while decoding a FLAC frame in FlacExtractor.read(), converting it into an IOException so the extractor pipeline treats it as an unrecoverable read failure for the current sample. It reports the exact stream position (lastDecodePosition) where the native decoder bailed out, which pins the failure to a specific frame in the input data.

Source

Thrown at extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java:154

  public int read(final ExtractorInput input, PositionHolder seekPosition) throws IOException {
    if (input.getPosition() == 0 && !id3MetadataDisabled && id3Metadata == null) {
      id3Metadata = FlacMetadataReader.peekId3Metadata(input, /* parseData= */ true);
    }

    FlacDecoderJni decoderJni = initDecoderJni(input);
    try {
      decodeStreamMetadata(input);

      if (binarySearchSeeker != null && binarySearchSeeker.isSeeking()) {
        return handlePendingSeek(input, seekPosition, outputBuffer, outputFrameHolder, trackOutput);
      }

      ByteBuffer outputByteBuffer = outputFrameHolder.byteBuffer;
      long lastDecodePosition = decoderJni.getDecodePosition();
      try {
        decoderJni.decodeSampleWithBacktrackPosition(outputByteBuffer, lastDecodePosition);
      } catch (FlacDecoderJni.FlacFrameDecodeException e) {
        throw new IOException("Cannot read frame at position " + lastDecodePosition, e);
      }
      int outputSize = outputByteBuffer.limit();
      if (outputSize == 0) {
        return RESULT_END_OF_INPUT;
      }

      outputSample(outputBuffer, outputSize, decoderJni.getLastFrameTimestamp(), trackOutput);
      return decoderJni.isEndOfData() ? RESULT_END_OF_INPUT : RESULT_CONTINUE;
    } finally {
      decoderJni.clearData();
    }
  }

  @Override
  public void seek(long position, long timeUs) {
    if (position == 0) {
      streamMetadataDecoded = false;
    }

View on GitHub (pinned to dd430f7053)

Solutions

  1. Verify the file plays with an independent tool (ffplay/flac -t) — if it also fails, the file is corrupted; re-rip or re-download the source.
  2. If the file is valid, check that the data being fed is complete: for progressive/HTTP sources inspect for truncation (Content-Length vs bytes read) and for local files compare checksums.
  3. Upgrade the FLAC extension / migrate to androidx.media3 — older libflac builds had stricter or buggier frame validation that has since been fixed.
  4. If corruption is expected in your environment, catch IOException around player preparation and surface a user-facing 'unsupported/corrupt media' error rather than retrying indefinitely.

Example fix

// before
player.setMediaItem(MediaItem.fromUri(corruptFlacUri)); // PlaybackException: Cannot read frame at position ...

// after
player.setMediaItem(MediaItem.fromUri(corruptFlacUri));
player.addListener(new Player.Listener() {
  @Override public void onPlayerError(PlaybackException error) {
    if (error.getErrorCode() == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED) {
      showUserMessage("Corrupted FLAC file: " + error.getMessage()); // includes frame position
    }
  }
});
Defensive patterns

Strategy: try-catch

Try / catch

// At player level:
player.addListener(new Player.Listener() {
  @Override public void onPlayerError(PlaybackException e) {
    if (e.getErrorCode() == PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED
        && e.getMessage() != null && e.getMessage().contains("Cannot read frame")) {
      skipToNextItem(); // corrupted FLAC: do not retry the same file
    }
  }
});

Prevention

When it happens

Trigger: FlacExtractor.read() calls decoderJni.decodeSampleWithBacktrackPosition(...); if the native libflac decoder rejects the frame (CRC mismatch, malformed subframe, or a mid-stream inconsistency between the frame header and the STREAMINFO block) the JNI layer throws FlacFrameDecodeException and it is rethrown as this IOException. Common with truncated or corrupted FLAC files and with files whose STREAMINFO disagrees with actual frames.

Common situations: Playing a partially downloaded or bit-flipped .flac; seeking in a stream produced by a broken encoder; extractor tests feeding hand-crafted FLAC data; live/progressive streams that are truncated at the network layer.

Related errors


AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14). Data as JSON: /api/errors/08be82fb8b5adfd2. Report an issue: GitHub.