{"record":{"id":"41617c7244759d1f","repo":"apache/hadoop","slug":"can-t-read-a-negative-number-of-bytes","errorCode":null,"errorMessage":"can't read a negative number of bytes.","messagePattern":"can't read a negative number of bytes\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java","lineNumber":1865,"sourceCode":"    }\n    closeCurrentBlockReaders();\n  }\n\n  /**\n   * The immutable empty buffer we return when we reach EOF when doing a\n   * zero-copy read.\n   */\n  private static final ByteBuffer EMPTY_BUFFER =\n      ByteBuffer.allocateDirect(0).asReadOnlyBuffer();\n\n  @Override\n  public synchronized ByteBuffer read(ByteBufferPool bufferPool,\n      int maxLength, EnumSet<ReadOption> opts)\n          throws IOException, UnsupportedOperationException {\n    if (maxLength == 0) {\n      return EMPTY_BUFFER;\n    } else if (maxLength < 0) {\n      throw new IllegalArgumentException(\"can't read a negative \" +\n          \"number of bytes.\");\n    }\n    if ((blockReader == null) || (blockEnd == -1)) {\n      if (pos >= getFileLength()) {\n        return null;\n      }\n      /*\n       * If we don't have a blockReader, or the one we have has no more bytes\n       * left to read, we call seekToBlockSource to get a new blockReader and\n       * recalculate blockEnd.  Note that we assume we're not at EOF here\n       * (we check this above).\n       */\n      if ((!seekToBlockSource(pos)) || (blockReader == null)) {\n        throw new IOException(\"failed to allocate new BlockReader \" +\n            \"at position \" + pos);\n      }\n    }\n    ByteBuffer buffer = null;","sourceCodeStart":1847,"sourceCodeEnd":1883,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java#L1847-L1883","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nint len = (int) Math.min(endOffset - curPos, maxChunk);\nin = enhancedIn.read(bufferPool, len, opts); // len < 0 when curPos > endOffset\n\n// after\nint len = (int) Math.min(endOffset - curPos, maxChunk);\nif (len < 0) len = 0; // or break the loop when curPos >= endOffset\nin = enhancedIn.read(bufferPool, len, opts);","handlingStrategy":"validation","validationCode":"int maxLength = (int) Math.min(limit - curPos, maxChunk);\nif (maxLength < 0) maxLength = 0; // API treats 0 as a valid no-op returning the empty buffer\nByteBuffer b = in.read(bufferPool, maxLength, opts);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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."],"tags":["hdfs","hdfs-client","zero-copy","validation","io"],"backgroundTag":"negative-read-length","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}