{"record":{"id":"15d59a5af6dab15d","repo":"apache/hadoop","slug":"this-feature-is-disabled-please-refer-to-dfs-cli","errorCode":null,"errorMessage":"This feature is disabled.  Please refer to dfs.client.block.write.replace-datanode-on-failure.enable configuration property.","messagePattern":"This feature is disabled\\.  Please refer to dfs\\.client\\.block\\.write\\.replace-datanode-on-failure\\.enable configuration property\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/datatransfer/ReplaceDatanodeOnFailure.java","lineNumber":107,"sourceCode":"  private interface Condition {\n\n    /** Is the condition satisfied? */\n    boolean satisfy(short replication, DatanodeInfo[] existings, int nExistings,\n                    boolean isAppend, boolean isHflushed);\n  }\n\n  private final Policy policy;\n  private final boolean bestEffort;\n\n  public ReplaceDatanodeOnFailure(Policy policy, boolean bestEffort) {\n    this.policy = policy;\n    this.bestEffort = bestEffort;\n  }\n\n  /** Check if the feature is enabled. */\n  public void checkEnabled() {\n    if (policy == Policy.DISABLE) {\n      throw new UnsupportedOperationException(\n          \"This feature is disabled.  Please refer to \"\n          + HdfsClientConfigKeys.BlockWrite.ReplaceDatanodeOnFailure.ENABLE_KEY\n          + \" configuration property.\");\n    }\n  }\n\n  /**\n   * Best effort means that the client will try to replace the failed datanode\n   * (provided that the policy is satisfied), however, it will continue the\n   * write operation in case that the datanode replacement also fails.\n   *\n   * @return Suppose the datanode replacement fails.\n   *     false: An exception should be thrown so that the write will fail.\n   *     true : The write should be resumed with the remaining datandoes.\n   */\n  public boolean isBestEffort() {\n    return bestEffort;\n  }","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocol/datatransfer/ReplaceDatanodeOnFailure.java#L89-L125","documentation":"Thrown by the HDFS client write pipeline when it needs to add a replacement datanode (a pipeline member was lost and the remaining replicas would fall below the minimum) but datanode replacement is switched off. dfs.client.block.write.replace-datanode-on-failure.enable=false maps to Policy.DISABLE, and checkEnabled() aborts the write instead of continuing with an under-replicated pipeline.","triggerScenarios":"During write/append/hflush a datanode in the pipeline dies, the policy condition is satisfied (e.g. DEFAULT condition: replication would drop below minReplication), and ReplaceDatanodeOnFailure.get(conf).checkEnabled() is invoked with policy == DISABLE because the enable property is false.","commonSituations":"Single-node or pseudo-distributed test clusters where the feature was disabled per old guidance ('set enable=false on small clusters') without also setting policy=NEVER; inherited production configs; appends after a datanode restart on tiny clusters.","solutions":["Set dfs.client.block.write.replace-datanode-on-failure.enable=true (or remove the property) in the client's hdfs-client.xml/core-site.xml so a replacement datanode can be added","On single-node clusters keep enable=false but set dfs.client.block.write.replace-datanode-on-failure.policy=NEVER so the replacement condition is never satisfied and checkEnabled() is never reached","Ensure the cluster has spare datanodes so a replacement actually exists when the policy fires","Retry the failed write after the config change; the setting is read from each client Configuration instance"],"exampleFix":"// before (hdfs-client.xml)\n<property>\n  <name>dfs.client.block.write.replace-datanode-on-failure.enable</name>\n  <value>false</value>\n</property>\n<property>\n  <name>dfs.client.block.write.replace-datanode-on-failure.policy</name>\n  <value>DEFAULT</value>\n</property>\n\n// after (single-node cluster)\n<property>\n  <name>dfs.client.block.write.replace-datanode-on-failure.enable</name>\n  <value>false</value>\n</property>\n<property>\n  <name>dfs.client.block.write.replace-datanode-on-failure.policy</name>\n  <value>NEVER</value>\n</property>\n\n// after (multi-node cluster)\n<property>\n  <name>dfs.client.block.write.replace-datanode-on-failure.enable</name>\n  <value>true</value>\n</property>","handlingStrategy":"validation","validationCode":"Configuration conf = new Configuration();\ntry {\n  ReplaceDatanodeOnFailure.get(conf).checkEnabled();\n} catch (UnsupportedOperationException e) {\n  throw new IllegalStateException(\n      \"Writes will fail on datanode loss: set \"\n      + \"dfs.client.block.write.replace-datanode-on-failure.enable=true \"\n      + \"or policy=NEVER\", e);\n}","typeGuard":null,"tryCatchPattern":"catch (UnsupportedOperationException e) when thrown from DFSOutputStream.write/hflush/append: report the replace-datanode-on-failure config to the operator instead of retrying the write blindly.","preventionTips":["On single-node clusters always pair enable=false with policy=NEVER","Smoke-test a small write followed by a datanode kill in staging to catch disabled pipeline recovery","Keep client hdfs-client.xml under config management so the flag is never silently disabled"],"tags":["hdfs","write-pipeline","datanode-replacement","configuration"],"backgroundTag":"pipeline-recovery-disabled","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}