apache/beam · error · CoderException
unable to deserialize record
Error message
unable to deserialize record
What it means
SerializableCoder.decode() uses Java ObjectInputStream to deserialize; if the class of the serialized object is not on the classpath (ClassNotFoundException) it wraps it in a CoderException 'unable to deserialize record'. This typically means the writer and reader environments have different class versions or the class was renamed/moved.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/coders/SerializableCoder.java:202
public Class<T> getRecordType() {
return type;
}
@Override
public void encode(T value, OutputStream outStream) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(outStream);
oos.writeObject(value);
oos.flush();
}
@Override
public T decode(InputStream inStream) throws IOException, CoderException {
try {
ObjectInputStream ois = new ObjectInputStream(inStream);
return type.cast(ois.readObject());
} catch (ClassNotFoundException e) {
throw new CoderException("unable to deserialize record", e);
}
}
/**
* {@inheritDoc}
*
* @throws NonDeterministicException always. Java serialization is not deterministic with respect
* to {@link Object#equals} for all types.
*/
@Override
public void verifyDeterministic() throws NonDeterministicException {
throw new NonDeterministicException(this, "Java Serialization may be non-deterministic.");
}
@Override
@SuppressWarnings("EqualsGetClass")
public boolean equals(@Nullable Object other) {
return !(other == null || getClass() != other.getClass())View on GitHub (pinned to 12126d8942)
Solutions
- Restore the original fully-qualified class name (and serialVersionUID) so ObjectInputStream can resolve it
- Re-run the pipeline from the source instead of resuming from serialized state
- Add a serialVersionUID and avoid renaming/moving classes between releases
- If shading, configure the shade plugin to keep the class name stable, or use a non-Java-serialization coder (Avro/Row)
Example fix
// before
class MyRecord { } // renamed from com.old.pkg.MyRecord; old state unreadable
// after
class MyRecord implements Serializable {
private static final long serialVersionUID = 1L;
}
// keep FQCN stable, or re-run from source Defensive patterns
Strategy: try-catch
Validate before calling
// Check the serialized class is resolvable: Class.forName("com.pkg.MyRecord") in the reading job Type guard
null
Try / catch
try { T v = coder.decode(in); } catch (CoderException e) { /* ClassNotFoundException cause: class missing/renamed; re-run from source */ } Prevention
- Declare serialVersionUID and keep class FQCNs stable across releases
- Avoid moving/renaming Serializable classes used in pipeline state
- Prefer explicit coders (Avro/Row) over Java serialization for durable data
When it happens
Trigger: Decoding bytes produced in a different job/environment where the serialized class has been renamed, moved packages, or removed; running an updated pipeline against old state; fat-jar shading changing class names (serialVersionUID/class name mismatch).
Common situations: Class refactoring between pipeline versions; streaming job resume after a deploy where the class changed; shaded/relocated classes breaking ObjectInputStream resolution.
Related errors
- unable to deserialize {description}
- NullableCoder expects either a byte valued 0 (null) or 1 (pr
- Invalid encoded string length: {}
- error when decoding a textual integer
- EOF encountered decoding a ValueKind
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/19a9934408449b47.
Report an issue: GitHub.