{"record":{"id":"fe45df508d80b050","repo":"apache/hadoop","slug":"blocklist-for-has-changed","errorCode":null,"errorMessage":"Blocklist for {} has changed!","messagePattern":"Blocklist for (.+?) has changed!","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java","lineNumber":301,"sourceCode":"    }\n  }\n\n  private LocatedBlocks fetchAndCheckLocatedBlocks(LocatedBlocks existing)\n      throws IOException {\n    LocatedBlocks newInfo = dfsClient.getLocatedBlocks(src, 0);\n\n    DFSClient.LOG.debug(\"newInfo = {}\", newInfo);\n    if (newInfo == null) {\n      throw new IOException(\"Cannot open filename \" + src);\n    }\n\n    if (existing != null) {\n      Iterator<LocatedBlock> oldIter =\n          existing.getLocatedBlocks().iterator();\n      Iterator<LocatedBlock> newIter = newInfo.getLocatedBlocks().iterator();\n      while (oldIter.hasNext() && newIter.hasNext()) {\n        if (!oldIter.next().getBlock().equals(newIter.next().getBlock())) {\n          throw new IOException(\"Blocklist for \" + src + \" has changed!\");\n        }\n      }\n    }\n\n    return newInfo;\n  }\n\n  private long getLastBlockLength(LocatedBlocks blocks) throws IOException{\n    long lastBlockBeingWrittenLength = 0;\n    if (!blocks.isLastBlockComplete()) {\n      final LocatedBlock last = blocks.getLastLocatedBlock();\n      if (last != null) {\n        if (last.getLocations().length == 0) {\n          if (last.getBlockSize() == 0) {\n            // if the length is zero, then no data has been written to\n            // datanode. So no need to wait for the locations.\n            return 0;\n          }","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSInputStream.java#L283-L319","documentation":"During an openInfo(true) refresh, the client walks the cached LocatedBlocks and the newly fetched ones in parallel and requires identical block IDs in order. Any mismatch throws this IOException: the inode at src now points at different blocks, i.e. the file was replaced (deleted and recreated, truncated to zero and rewritten) while this stream held the old block list.","triggerScenarios":"A read-path block refresh on a file that another client concurrently recreated under the same name; overwrite-style writers (write temp, delete target, rename) racing a reader that already opened the old inode.","commonSituations":"Reading job output while a rerun job overwrites it (mapreduce output overwrite); log rotation implemented as delete+recreate; concurrent truncate followed by rewrite.","solutions":["Reopen the stream to pick up the new inode/block list, then re-seek","Have writers publish new versions with atomic rename instead of delete+recreate so readers keep a stable inode","Use append to grow files rather than recreating them under the same path"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":"static boolean isBlocklistChanged(IOException e) {\n  return e.getMessage() != null && e.getMessage().contains(\"has changed\");\n}","tryCatchPattern":"try {\n  /* read from in */\n} catch (IOException e) {\n  if (isBlocklistChanged(e)) {\n    in.close();\n    in = fs.open(path);   // pick up the new inode/block list\n    in.seek(pos);\n  } else throw e;\n}","preventionTips":["Publish new file versions via atomic rename instead of delete+recreate","Do not overwrite files that have active readers","Re-open streams when producers signal file replacement"],"tags":["hdfs","concurrency","file-replaced","read"],"backgroundTag":"concurrent-file-modification","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}