apache/hadoop · error · IllegalArgumentException
can't read a negative number of bytes.
Error message
can't read a negative number of bytes.
What it means
DFSInputStream.read(ByteBufferPool, int maxLength, EnumSet<ReadOption>) - the enhanced/zero-copy read entry point - rejects maxLength < 0 with IllegalArgumentException before doing anything (maxLength == 0 returns a shared empty buffer). Callers reach this via ByteBufferUtil.fallbackRead or by calling the enhanced read API directly, e.g. FSDataInputStream.read(ByteBufferPool, int, EnumSet) implementations in frameworks. A negative length is always a caller-side arithmetic bug; no cluster state can produce it.
Source
Thrown at hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java:1865
}
closeCurrentBlockReaders();
}
/**
* The immutable empty buffer we return when we reach EOF when doing a
* zero-copy read.
*/
private static final ByteBuffer EMPTY_BUFFER =
ByteBuffer.allocateDirect(0).asReadOnlyBuffer();
@Override
public synchronized ByteBuffer read(ByteBufferPool bufferPool,
int maxLength, EnumSet<ReadOption> opts)
throws IOException, UnsupportedOperationException {
if (maxLength == 0) {
return EMPTY_BUFFER;
} else if (maxLength < 0) {
throw new IllegalArgumentException("can't read a negative " +
"number of bytes.");
}
if ((blockReader == null) || (blockEnd == -1)) {
if (pos >= getFileLength()) {
return null;
}
/*
* If we don't have a blockReader, or the one we have has no more bytes
* left to read, we call seekToBlockSource to get a new blockReader and
* recalculate blockEnd. Note that we assume we're not at EOF here
* (we check this above).
*/
if ((!seekToBlockSource(pos)) || (blockReader == null)) {
throw new IOException("failed to allocate new BlockReader " +
"at position " + pos);
}
}
ByteBuffer buffer = null;View on GitHub (pinned to 2add963021)
Solutions
- Clamp the computed length: int len = (int) Math.max(0, Math.min(limit - pos, cap)); before calling the enhanced read.
- Find the subtraction producing the negative value (usually endOffset - curOffset or budget - consumed) and validate its operands.
- Return early on len == 0 - the API treats it as a valid no-op returning the empty buffer, so a zero-length chunk needs no special casing.
Example fix
// before int len = (int) Math.min(endOffset - curPos, maxChunk); in = enhancedIn.read(bufferPool, len, opts); // len < 0 when curPos > endOffset // after int len = (int) Math.min(endOffset - curPos, maxChunk); if (len < 0) len = 0; // or break the loop when curPos >= endOffset in = enhancedIn.read(bufferPool, len, opts);
Defensive patterns
Strategy: validation
Validate before calling
int maxLength = (int) Math.min(limit - curPos, maxChunk); if (maxLength < 0) maxLength = 0; // API treats 0 as a valid no-op returning the empty buffer ByteBuffer b = in.read(bufferPool, maxLength, opts);
Prevention
- Never pass a subtraction result to a length parameter without clamping at 0.
- Compute chunk budgets as Math.max(0, ...) at the source, not at the call site.
- Unit-test read sizing at boundaries: 0, 1, cap, cap+1, and underflowed inputs.
When it happens
Trigger: Passing a negative maxLength computed as a subtraction that underflowed, e.g. (int)(limit - position) where position > limit, or (available() - headerSize) where headerSize > available(). Also direct calls like in.read(pool, -1, opts).
Common situations: Custom zero-copy consumers porting from Hadoop 2.x APIs; chunked transfer loops that size the next read as (endOffset - streamPos) without clamping; unit tests exercising boundary sizes that accidentally feed 0-length pools with negative budgets.
Related errors
- Cannot seek to negative offset
- Mark/reset not supported
- tried to release a buffer that was not created by this strea
- Invalid value: bytesPerChecksum = {} <= 0
- Invalid values: dfs.bytes-per-checksum (={}) must divide blo
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/41617c7244759d1f.
Report an issue: GitHub.