{"record":{"id":"05473a3318c07c73","repo":"apache/hadoop","slug":"reducer-has-been-already-set","errorCode":null,"errorMessage":"Reducer has been already set","messagePattern":"Reducer has been already set","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java","lineNumber":661,"sourceCode":"        outputKeyClass, outputValueClass, index, prefix);\n\n    setMapperConf(isMap, jobConf, inputKeyClass, inputValueClass,\n        outputKeyClass, outputValueClass, mapperConf, index, prefix);\n  }\n\n  // if a reducer chain check the Reducer has been already set or not\n  protected static void checkReducerAlreadySet(boolean isMap,\n      Configuration jobConf, String prefix, boolean shouldSet) {\n    if (!isMap) {\n      if (shouldSet) {\n        if (jobConf.getClass(prefix + CHAIN_REDUCER_CLASS, null) == null) {\n          throw new IllegalStateException(\n              \"A Mapper can be added to the chain only after the Reducer has \"\n                  + \"been set\");\n        }\n      } else {\n        if (jobConf.getClass(prefix + CHAIN_REDUCER_CLASS, null) != null) {\n          throw new IllegalStateException(\"Reducer has been already set\");\n        }\n      }\n    }\n  }\n\n  protected static void validateKeyValueTypes(boolean isMap,\n      Configuration jobConf, Class<?> inputKeyClass, Class<?> inputValueClass,\n      Class<?> outputKeyClass, Class<?> outputValueClass, int index,\n      String prefix) {\n    // if it is a reducer chain and the first Mapper is being added check the\n    // key and value input classes of the mapper match those of the reducer\n    // output.\n    if (!isMap && index == 0) {\n      Configuration reducerConf = getChainElementConf(jobConf, prefix\n          + CHAIN_REDUCER_CONFIG);\n      if (!inputKeyClass.isAssignableFrom(reducerConf.getClass(\n          REDUCER_OUTPUT_KEY_CLASS, null))) {\n        throw new IllegalArgumentException(\"The Reducer output key class does\"","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/lib/chain/Chain.java#L643-L679","documentation":"The mirror rule of the ordering check: checkReducerAlreadySet(shouldSet=false) throws IllegalStateException(\"Reducer has been already set\") when the reducer-side chain is being (re)configured while the 'chain reducer class' key is already present - in practice, when ChainReducer.setReducer(...) is called a second time on the same Job/Configuration.","triggerScenarios":"Two ChainReducer.setReducer(...) calls on one Job/Configuration: submission-retry logic reusing a Configuration, job templates merged on top of an already-wired conf, or copy-pasted setup blocks.","commonSituations":"Job assembly code that 'reconfigures' a job for retry instead of building a fresh Job; combining config snippets from multiple sources that each set the chain reducer; loops that set the reducer per input.","solutions":["Call ChainReducer.setReducer exactly once per Job; build a fresh Job/Configuration for each submission attempt.","Clear the chain keys (or, better, start from a new Configuration) when re-assembling a job.","Move chain wiring out of loops/retry paths into single-execution setup code."],"exampleFix":"// before: retry path reuses the same job conf\nChainReducer.setReducer(job, Reduce.class, ...);   // first attempt\nChainReducer.setReducer(job, Reduce2.class, ...);  // retry -> IllegalStateException\n\n// after: fresh job per submission\nJob job = Job.getInstance(new Configuration(conf));\nChainReducer.setReducer(job, Reduce2.class, ...);","handlingStrategy":"validation","validationCode":"// guard submission-retry paths\nboolean alreadyWired = conf.get(\"chain.reducer.class\") != null;\nif (alreadyWired) {\n  job = Job.getInstance(new Configuration(conf)); // fresh job, then setReducer once\n}\nChainReducer.setReducer(job, Reduce.class, ...);","typeGuard":null,"tryCatchPattern":"catch (IllegalStateException e) with message \"Reducer has been already set\": discard the Job and rebuild from a fresh Configuration instead of trying to overwrite the existing reducer.","preventionTips":["Build a fresh Job/Configuration for every submission attempt.","Call ChainReducer.setReducer exactly once per job.","Keep chain wiring out of loops and retry blocks."],"tags":["hadoop","mapreduce","chain","duplicate-setup","illegalstate"],"backgroundTag":"duplicate-configuration","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}