apache/hadoop · critical · RuntimeException
readObject can't find class {className}
Error message
readObject can't find class {className} What it means
Thrown by ObjectWritable.loadClass(Configuration, String) when the class named in the serialized stream cannot be resolved: Configuration.getClassByName(className) (or Class.forName when conf is null) raises ClassNotFoundException, which is rethrown as this RuntimeException. ObjectWritable stores the concrete class NAME inside the stream for Writable values; on read, that class must exist on the reader's classpath.
Source
Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/ObjectWritable.java:420
/**
* Find and load the class with given name <code>className</code> by first finding
* it in the specified <code>conf</code>. If the specified <code>conf</code> is null,
* try load it directly.
*
* @param conf configuration.
* @param className classname.
* @return Class.
*/
public static Class<?> loadClass(Configuration conf, String className) {
Class<?> declaredClass = null;
try {
if (conf != null)
declaredClass = conf.getClassByName(className);
else
declaredClass = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("readObject can't find class " + className,
e);
}
return declaredClass;
}
@Override
public void setConf(Configuration conf) {
this.conf = conf;
}
@Override
public Configuration getConf() {
return this.conf;
}
}
View on GitHub (pinned to 2add963021)
Solutions
- Add the jar containing the named class to the reader's classpath (cluster client classpath, job jar lib/, or DistributedCache).
- Align Hadoop/dependency versions between writer and reader if the class moved between versions.
- If you renamed the class, either keep a serialization-compatible alias or rewrite the data.
- Pass a non-null Configuration so class resolution uses the Hadoop classpath (conf.getClassByName) rather than only the system classloader.
Example fix
// before: reading fails, class only on writer's classpath
ObjectWritable ow = new ObjectWritable();
ow.setConf(conf);
ow.readFields(in); // RuntimeException: readObject can't find class com.example.MyValue
// after: ship and register the jar before deserializing
job.addFileToClassPath(new Path("/libs/mytypes-1.0.jar"), fs, conf);
// or on a client: java -cp mytypes-1.0.jar:hadoop-classpath ... Defensive patterns
Strategy: validation
Validate before calling
try {
conf.getClassByName(className); // or ObjectWritable.loadClass(conf, className)
} catch (ClassNotFoundException e) {
throw new IllegalStateException(
"Class " + className + " from payload missing on this JVM — "
+ "add its jar to the classpath/job jar", e);
}
// safe to readFields now Type guard
static boolean isClassResolvable(Configuration conf, String className) {
try {
ObjectWritable.loadClass(conf, className);
return true;
} catch (RuntimeException e) {
return e.getCause() instanceof ClassNotFoundException ? false : true; // rethrow others
}
} Try / catch
try {
ObjectWritable ow = new ObjectWritable();
ow.setConf(conf);
ow.readFields(in);
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().startsWith("readObject can't find class")) {
failWithClasspathHint(e.getMessage()); // name the missing class and expected jar
}
throw e;
} Prevention
- Ship all custom Writable classes in the job jar's lib/ and via DistributedCache so every node can resolve them.
- Pass a Configuration into the read path (never null) so Hadoop's classpath is used, not just the system classloader.
- When renaming/moving classes, keep data compatibility in mind — rewrite stored data or keep a compatible name.
- Log the embedded class name from the exception message; it tells you exactly which jar is missing.
When it happens
Trigger: Deserializing an ObjectWritable/RPC payload whose embedded class name (e.g. com.example.MyWritable) is absent from the reading JVM's classpath; running with conf == null so only Class.forName on the system classloader is used; class renamed/moved between writer and reader versions.
Common situations: Client JVM missing the application jar that defines custom Writable types used in RPC responses; Hadoop/dependency version skew where a class moved packages; shaded/relocated jars changing class names; distributed cache not shipping the job jar to nodes doing the deserialization.
Related errors
- Can't write: {instance} as {declaredClass}
- Socket Factory class not found: ${cnfe}
- Cannot initialize the class: {clazz}
- Not a primitive: {declaredClass}
- Invalid size: {size}
AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22).
Data as JSON: /api/errors/88df2a218f7bf7a3.
Report an issue: GitHub.