apache/hadoop · error · EOFException

Cannot seek after EOF

Error message

Cannot seek after EOF

What it means

DFSStripedInputStream.seek(long) validates the target exactly like the replicated stream: targetPos > getFileLength() throws EOFException('Cannot seek after EOF') (and negative targets / closed streams get their own errors just below). Because it compares against the live file length, a seek that was in-bounds against a stale cached length becomes 'after EOF' when the EC file has been truncated or is simply shorter than assumed.

Source

Thrown at hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSStripedInputStream.java:357

  void updateReadStats(final StripedBlockUtil.BlockReadStats stats, long readTimeMS) {
    if (stats == null) {
      return;
    }
    updateReadStatistics(readStatistics, stats.getBytesRead(),
        stats.isShortCircuit(), stats.getNetworkDistance());
    dfsClient.updateFileSystemReadStats(stats.getNetworkDistance(),
        stats.getBytesRead(), readTimeMS);
    assert readStatistics.getBlockType() == BlockType.STRIPED;
    dfsClient.updateFileSystemECReadStats(stats.getBytesRead());
  }

  /**
   * Seek to a new arbitrary location.
   */
  @Override
  public synchronized void seek(long targetPos) throws IOException {
    if (targetPos > getFileLength()) {
      throw new EOFException("Cannot seek after EOF");
    }
    if (targetPos < 0) {
      throw new EOFException("Cannot seek to negative offset");
    }
    if (closed.get()) {
      throw new IOException("Stream is closed!");
    }
    if (targetPos <= blockEnd) {
      final long targetOffsetInBlk = getOffsetInBlockGroup(targetPos);
      if (curStripeRange.include(targetOffsetInBlk)) {
        int bufOffset = getStripedBufOffset(targetOffsetInBlk);
        curStripeBuf.position(bufOffset);
        pos = targetPos;
        return;
      }
    }
    pos = targetPos;
    blockEnd = -1;

View on GitHub (pinned to 2add963021)

Solutions

  1. Validate against a fresh length before seeking: long len = fs.getFileStatus(path).getLen(); if (targetPos > len) handle/throw your own error.
  2. If file lengths can change concurrently, make rewrites atomic (write temp, rename) so readers never see shrinking files, and re-stat on any EOF/seek failure.
  3. Clamp in defensive code: in.seek(Math.min(targetPos, len)) when the tail is optional.
  4. When seeking to a footer, first check len >= footerSize and surface a clear 'file too small' error.

Example fix

// before
in.seek(cachedFileLen - trailerSize); // EOFException when file shrank

// after
long len = fs.getFileStatus(path).getLen();
if (len < trailerSize) throw new IllegalStateException(path + " too small");
in.seek(len - trailerSize);
Defensive patterns

Strategy: validation

Validate before calling

long fileLen = fs.getFileStatus(path).getLen(); // do not trust a cached length
if (targetPos < 0 || targetPos > fileLen) {
  throw new IllegalArgumentException(
      "seek target " + targetPos + " outside [0, " + fileLen + "] for " + path);
}
stripedIn.seek(targetPos);

Try / catch

try {
  stripedIn.seek(targetPos);
} catch (EOFException e) {
  long freshLen = fs.getFileStatus(path).getLen();
  if (targetPos > freshLen) throw new IllegalStateException(
      "file shrank under reader: wanted " + targetPos + ", len " + freshLen, e);
  throw e; // transient inconsistency - re-stat and retry once
}

Prevention

When it happens

Trigger: Calling seek(n) on a striped/erasure-coded file where n exceeds the file length - e.g. seek(cachedLen) after the file was rewritten shorter; seeking to footer offsets computed from stale metadata; seeking past EOF instead of checking remaining length first.

Common situations: Readers over EC-enabled warehouse paths whose files are compacted/truncated under them; index files rebuilt smaller than the cached offset table expects; porting code from replicated files where the same bug threw a different exception that happened to be caught.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/c0f49654492529d6. Report an issue: GitHub.