apache/hadoop · error · IOException
No RecordReader for " + ident
Error message
No RecordReader for " + ident
What it means
WNode.getRecordReader looks up the ComposableRecordReader constructor registered for the wrapped node's identifier and throws IOException("No RecordReader for <ident>") when rrCstrMap has no entry. In the stock flow CompositeInputFormat.setFormat calls addDefaults(), registering tbl to WrappedRecordReader before any reader is built, so this fires only when the parser tree is driven without that registration — custom code reusing Parser.WNode directly, or an extension that registers a node type without its record reader.
Source
Thrown at hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/join/Parser.java:326
}
private JobConf getConf(JobConf job) {
JobConf conf = new JobConf(job);
FileInputFormat.setInputPaths(conf, indir);
conf.setClassLoader(job.getClassLoader());
return conf;
}
public InputSplit[] getSplits(JobConf job, int numSplits)
throws IOException {
return inf.getSplits(getConf(job), numSplits);
}
public ComposableRecordReader getRecordReader(
InputSplit split, JobConf job, Reporter reporter) throws IOException {
try {
if (!rrCstrMap.containsKey(ident)) {
throw new IOException("No RecordReader for " + ident);
}
return rrCstrMap.get(ident).newInstance(id,
inf.getRecordReader(split, getConf(job), reporter), cmpcl);
} catch (IllegalAccessException e) {
throw (IOException)new IOException().initCause(e);
} catch (InstantiationException e) {
throw (IOException)new IOException().initCause(e);
} catch (InvocationTargetException e) {
throw (IOException)new IOException().initCause(e);
}
}
public String toString() {
return ident + "(" + inf.getClass().getName() + ",\"" + indir + "\")";
}
}
/**View on GitHub (pinned to 2add963021)
Solutions
- Go through CompositeInputFormat.setFormat(job) (or CompositeInputFormat.getRecordReader) so addDefaults() registers inner/outer/override/tbl first
- In extensions, always register node and reader together via WNode.addIdentifier / CNode.addIdentifier
- Ensure the join package is loaded by a single classloader in the JVM
Example fix
// before: node tree driven raw Parser.Node root = /* obtained directly */; root.getRecordReader(split, job, reporter); // No RecordReader for tbl // after: let CompositeInputFormat register identifiers first CompositeInputFormat<?> cif = new CompositeInputFormat<Object>(); cif.setFormat(job); ComposableRecordReader<?, ?> rr = cif.getRecordReader(split, job, reporter);
Defensive patterns
Strategy: validation
Validate before calling
// Always initialize through CompositeInputFormat so identifiers are registered
CompositeInputFormat<Text> cif = new CompositeInputFormat<Text>();
cif.setFormat(job); // registers inner/outer/override/tbl via addDefaults()
ComposableRecordReader<Text, TupleWritable> rr =
cif.getRecordReader(split, job, reporter); Try / catch
try {
return root.getRecordReader(split, job, reporter);
} catch (IOException e) {
throw new IOException("identifier not registered; call CompositeInputFormat.setFormat first", e);
} Prevention
- Never call node.getRecordReader on a raw Parser tree; go through CompositeInputFormat
- Register node and reader classes together via addIdentifier in extensions
- Avoid loading the join package from multiple classloaders
When it happens
Trigger: Calling getRecordReader on a Parser node tree obtained outside CompositeInputFormat without calling setFormat first; a custom ComposableInputFormat that populates the node map but not the record-reader map; classpath skew where org.apache.hadoop.mapred.join classes load from two classloaders so the static maps diverge.
Common situations: Framework extensions and unit tests that hand-drive the parser; isolated-classloader setups such as uber-jars or nested containers.
Related errors
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/c4c1342e3d594e20.
Report an issue: GitHub.