{"record":{"id":"1ecd30ed66da7db0","repo":"apache/hadoop","slug":"a-mapper-can-be-added-to-the-chain-only-after-the","errorCode":null,"errorMessage":"A Mapper can be added to the chain only after the Reducer has been set","messagePattern":"A Mapper can be added to the chain only after the Reducer has been 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":655,"sourceCode":"\n    // set the mapper class\n    int index = getIndex(jobConf, prefix);\n    jobConf.setClass(prefix + CHAIN_MAPPER_CLASS + index, klass, Mapper.class);\n\n    validateKeyValueTypes(isMap, jobConf, inputKeyClass, inputValueClass,\n        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.","sourceCodeStart":637,"sourceCodeEnd":673,"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#L637-L673","documentation":"ChainReducer composes map-reduce-map pipelines; the reducer must be registered before any reducer-side mapper. checkReducerAlreadySet(shouldSet=true) throws IllegalStateException(\"A Mapper can be added to the chain only after the Reducer has been set\") when ChainReducer.addMapper(...) is called while the 'chain reducer class' key is absent from the job configuration, i.e. ChainReducer.setReducer(...) has not been invoked yet.","triggerScenarios":"Calling ChainReducer.addMapper(job, ...) before ChainReducer.setReducer(job, ...) on the same Job/Configuration; or setReducer arguments (e.g. reducerClass) wrong so the class key was never stored. Only applies to the reducer-side chain (!isMap); ChainMapper has no such ordering rule.","commonSituations":"Refactors reordering builder calls; adapting a ChainMapper example into a ChainReducer flow; copying job-assembly code that sets the mapper chain first out of habit.","solutions":["Call ChainReducer.setReducer(job, Reducer.class, inK, inV, outK, outV, byValue) first, then every ChainReducer.addMapper(...), then submit.","Centralize the wiring order in one builder method so call order cannot regress.","Ensure setReducer's class/key/value arguments are non-null so registration actually stores the reducer."],"exampleFix":"// before\nChainReducer.addMapper(job, CleanupMapper.class,\n    Text.class, Text.class, Text.class, Text.class, false); // IllegalStateException\nChainReducer.setReducer(job, Reduce.class,\n    LongWritable.class, Text.class, Text.class, Text.class, false);\n\n// after\nChainReducer.setReducer(job, Reduce.class,\n    LongWritable.class, Text.class, Text.class, Text.class, false);\nChainReducer.addMapper(job, CleanupMapper.class,\n    Text.class, Text.class, Text.class, Text.class, false);","handlingStrategy":"validation","validationCode":"// enforce ordering in one builder method\nstatic void wireChain(Job job) {\n  ChainReducer.setReducer(job, Reduce.class,\n      LongWritable.class, Text.class, Text.class, IntWritable.class, false);\n  ChainReducer.addMapper(job, PostMap.class,\n      Text.class, IntWritable.class, Text.class, Text.class, false);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always call ChainReducer.setReducer before any ChainReducer.addMapper on the same job.","Keep chain wiring in a single method so ordering cannot be broken by refactors.","Smoke-test job assembly in CI by wiring the chain against a test Configuration."],"tags":["hadoop","mapreduce","chain","call-order","illegalstate"],"backgroundTag":"api-call-order","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}