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

  1. Restore the original fully-qualified class name (and serialVersionUID) so ObjectInputStream can resolve it
  2. Re-run the pipeline from the source instead of resuming from serialized state
  3. Add a serialVersionUID and avoid renaming/moving classes between releases
  4. 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

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


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/19a9934408449b47. Report an issue: GitHub.