apache/beam · error · CoderException

Cannot encode a null value.

Error message

Cannot encode a null value.

What it means

KryoCoder.encode() does not support null values; it deliberately throws CoderException("Cannot encode a null value.") before invoking Kryo serialization. Beam coders conventionally reject nulls — nulls must be handled at the PCollection/schema level, not inside the coder.

Solutions

  1. Filter out null elements before they reach the coder: pipeline.apply(Filter.by(e -> e != null))
  2. Emit a sentinel/wrapper object (e.g. Optional or a Present/Absent type) instead of null
  3. Fix the producing DoFn to skip null results rather than returning them
  4. If nulls are legitimate, wrap in a Nullable-aware container type that Kryo can encode

Example fix

// before
return parse(record); // may return null
// after
MyValue v = parse(record);
return v == null ? null : v; // use Filter.by(v -> v != null) downstream, or return Optional-wrapped value
Defensive patterns

Strategy: type-guard

Validate before calling

java
pcollection.apply(Filter.by(v -> v != null));

Type guard

java
static <T> boolean isPresent(T v) { return v != null; }

Try / catch

java
try {
  coder.encode(value, out);
} catch (CoderException e) {
  if ("Cannot encode a null value.".equals(e.getMessage())) {
    // nulls are unsupported: fix upstream DoFn/filter instead of retrying
  }
}

Prevention

When it happens

Trigger: Encoding a PCollection element that is null at runtime — e.g. a KV/DoFn emitted null, a parse produced null, or a nullable field feeding a Kryo-coded PCollection.

Common situations: Map/DoFn returning null for filtered records instead of dropping them; JSON/CSV parsing yielding null rows; joins producing null values that flow into the Kryo-coded collection.

Related errors


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

Appendix: source

Thrown at sdks/java/extensions/kryo/src/main/java/org/apache/beam/sdk/extensions/kryo/KryoCoder.java:196

  /** Unique id of the {@link KryoCoder} instance. */
  private final String instanceId = UUID.randomUUID().toString();

  /** Options for underlying kryo instance. */
  private final SerializableOptions options;

  /** Client-defined class registrations to {@link Kryo}. */
  private final List<KryoRegistrar> registrars;

  private KryoCoder(SerializableOptions options, List<KryoRegistrar> registrars) {
    this.options = options;
    this.registrars = registrars;
  }

  @Override
  public void encode(T value, OutputStream outStream) throws IOException {
    final KryoState kryoState = KryoState.get(this);
    if (value == null) {
      throw new CoderException("Cannot encode a null value.");
    }
    final OutputChunked outputChunked = kryoState.getOutputChunked();
    outputChunked.setOutputStream(outStream);
    try {
      kryoState.getKryo().writeClassAndObject(outputChunked, value);
      outputChunked.endChunk();
      outputChunked.flush();
    } catch (KryoException e) {
      outputChunked.reset();
      if (e.getCause() instanceof EOFException) {
        throw (EOFException) e.getCause();
      }
      throw new CoderException("Cannot encode given object of type [" + value.getClass() + "].", e);
    } catch (IllegalArgumentException e) {
      String message = e.getMessage();
      if (message != null) {
        if (message.startsWith("Class is not registered")) {
          throw new CoderException(message);

View on GitHub (pinned to 12126d8942)