{"record":{"id":"4f3c0c00a29228fa","repo":"apache/hadoop","slug":"the-reducer-output-key-class-does-not-match-the-ma","errorCode":null,"errorMessage":"The Reducer output key class does not match the Mapper input key class","messagePattern":"The Reducer output key class does not match the Mapper input key class","errorType":"exception","errorClass":"IllegalArgumentException","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":679,"sourceCode":"          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\"\n            + \" not match the Mapper input key class\");\n      }\n      if (!inputValueClass.isAssignableFrom(reducerConf.getClass(\n          REDUCER_OUTPUT_VALUE_CLASS, null))) {\n        throw new IllegalArgumentException(\"The Reducer output value class\"\n            + \" does not match the Mapper input value class\");\n      }\n    } else if (index > 0) {\n      // check the that the new Mapper in the chain key and value input classes\n      // match those of the previous Mapper output.\n      Configuration previousMapperConf = getChainElementConf(jobConf, prefix\n          + CHAIN_MAPPER_CONFIG + (index - 1));\n      if (!inputKeyClass.isAssignableFrom(previousMapperConf.getClass(\n          MAPPER_OUTPUT_KEY_CLASS, null))) {\n        throw new IllegalArgumentException(\"The specified Mapper input key class does\"\n            + \" not match the previous Mapper's output key class.\");\n      }\n      if (!inputValueClass.isAssignableFrom(previousMapperConf.getClass(","sourceCodeStart":661,"sourceCodeEnd":697,"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#L661-L697","documentation":"When the first reducer-side mapper is added (index 0), Chain validates type continuity of the pipeline: the reducer's configured output key/value classes must be assignable to the new mapper's input key/value classes. validateKeyValueTypes throws IllegalArgumentException(\"The Reducer output key class does not match the Mapper input key class\") (and the analogous value message) when inputKeyClass.isAssignableFrom(reducer output key class) fails.","triggerScenarios":"ChainReducer.setReducer(job, R.class, kIn, vIn, Text.class, IntWritable.class, ...) followed by ChainReducer.addMapper(job, M.class, LongWritable.class, ...) - the first mapper's declared input key (LongWritable) does not match the reducer's declared output key (Text). Also triggered by swapping the four class arguments of either call.","commonSituations":"Type drift after refactoring mapper/reducer signatures; mis-ordering the (inputKey, inputValue, outputKey, outputValue) parameters, which are easy to mix up since they are all Class tokens checked only at wiring time.","solutions":["Make the first reducer-side mapper's input key/value classes exactly the reducer's output key/value classes (or supertypes).","Re-check the parameter order of both calls: setReducer(job, reducer, inK, inV, outK, outV, byValue) and addMapper(job, mapper, inK, inV, outK, outV, byValue).","Add a wiring unit test that calls setReducer + addMapper against a test Configuration so mismatches fail in CI, not at job submission.","Update adjacent chain steps after any mapper/reducer signature change (every adjacent pair must agree)."],"exampleFix":"// before: reducer outputs Text, mapper claims LongWritable input\nChainReducer.setReducer(job, Reduce.class,\n    LongWritable.class, Text.class, Text.class, IntWritable.class, false);\nChainReducer.addMapper(job, PostMap.class,\n    LongWritable.class, IntWritable.class, Text.class, Text.class, false); // throws\n\n// after: first mapper input matches reducer output\nChainReducer.addMapper(job, PostMap.class,\n    Text.class, IntWritable.class, Text.class, Text.class, false);","handlingStrategy":"type-guard","validationCode":"static void checkChainTypes(Class<?> reducerOutK, Class<?> reducerOutV,\n                                 Class<?> mapperInK, Class<?> mapperInV) {\n  if (!mapperInK.isAssignableFrom(reducerOutK) || !mapperInV.isAssignableFrom(reducerOutV)) {\n    throw new IllegalArgumentException(\"First mapper input [\" + mapperInK + \",\" + mapperInV\n        + \"] must match reducer output [\" + reducerOutK + \",\" + reducerOutV + \"]\");\n  }\n}","typeGuard":"boolean chainTypesCompatible(Class<?> reducerOutK, Class<?> reducerOutV,\n                               Class<?> mapperInK, Class<?> mapperInV) {\n  return mapperInK.isAssignableFrom(reducerOutK)\n      && mapperInV.isAssignableFrom(reducerOutV);\n}","tryCatchPattern":null,"preventionTips":["Make the first reducer-side mapper's input classes identical to the reducer's output classes.","Memorize the parameter order (inK, inV, outK, outV) for both setReducer and addMapper; wrong order is the top cause.","Add a CI wiring test for every chain; update adjacent steps whenever a mapper/reducer signature changes."],"tags":["hadoop","mapreduce","chain","type-mismatch","generics","illegalargument"],"backgroundTag":"generic-type-mismatch","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}