{"record":{"id":"17aeb934732c4427","repo":"apache/hadoop","slug":"invalid-negative-number-of-blocks","errorCode":null,"errorMessage":"invalid negative number of blocks","messagePattern":"invalid negative number of blocks","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java","lineNumber":694,"sourceCode":"          this.erasureCodingPolicyId =\n              ErasureCodeConstants.REPLICATION_POLICY_ID;\n        }\n        // read clientId and callId\n        readRpcIds(in, logVersion);\n      } else {\n        this.clientName = \"\";\n        this.clientMachine = \"\";\n      }\n    }\n\n    static final public int MAX_BLOCKS = 1024 * 1024 * 64;\n    \n    private static Block[] readBlocks(\n        DataInputStream in,\n        int logVersion) throws IOException {\n      int numBlocks = in.readInt();\n      if (numBlocks < 0) {\n        throw new IOException(\"invalid negative number of blocks\");\n      } else if (numBlocks > MAX_BLOCKS) {\n        throw new IOException(\"invalid number of blocks: \" + numBlocks +\n            \".  The maximum number of blocks per file is \" + MAX_BLOCKS);\n      }\n      Block[] blocks = new Block[numBlocks];\n      for (int i = 0; i < numBlocks; i++) {\n        Block blk = new Block();\n        blk.readFields(in);\n        blocks[i] = blk;\n      }\n      return blocks;\n    }\n\n    public String stringifyMembers() {\n      StringBuilder builder = new StringBuilder();\n      builder.append(\"[length=\")\n          .append(length)\n          .append(\", inodeId=\")","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLogOp.java#L676-L712","documentation":"readBlocks reads the block count of an OP_ADD or OP_CLOSE record; a negative count cannot occur in valid data, so it throws IOException('invalid negative number of blocks'). In practice the stream position is wrong: earlier corruption desynchronized the reader and the count field now holds arbitrary bits.","triggerScenarios":"Decoding a corrupt or truncated Add/Close record where garbage lands in the numBlocks field; reading a segment under wrong layout assumptions after desync.","commonSituations":"Torn final segment after a NameNode crash; disk corruption; partially copied segment files.","solutions":["Run 'hdfs namenode -recover' so the loader resyncs past the bad record","Validate the segment with 'hdfs oev' and restore fsimage plus edits from backup if it fails","Re-copy any segment that was transferred partially (compare sizes and checksums)"],"exampleFix":"# before: replay fails 'invalid negative number of blocks'\nhdfs --daemon start namenode\n\n# after: confirm the bad segment, then recover\nhdfs oev -i <segment> -o /tmp/check.xml -p xml   # fails at the same offset\nhdfs namenode -recover","handlingStrategy":"try-catch","validationCode":"hdfs oev -i <segment> -o /tmp/check.xml -p xml   # parse fails at the desynchronized record","typeGuard":null,"tryCatchPattern":"try {\n  loader.loadFSEdits(storage, 0);\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"invalid negative number of blocks\")) {\n    // reader desynchronized on corrupt bytes: 'hdfs namenode -recover'\n    // resyncs past it, or restore fsimage+edits from backup\n  }\n  throw e;\n}","preventionTips":["Verify file sizes after copying segments between hosts","Treat a negative count as proof of upstream corruption, never as valid data"],"tags":["hdfs","namenode","edit-log","corruption","blocks","data-format"],"backgroundTag":"edit-log-corruption","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}