apache/beam · error · CoderException
cannot encode a null ReadableDuration
Error message
cannot encode a null ReadableDuration
What it means
DurationCoder.encode rejects null ReadableDuration values with CoderException: the coder encodes durations as a VarLong of milliseconds and has no null representation. Null durations must be handled upstream or with a nullable wrapper coder.
Source
Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/coders/DurationCoder.java:60
new TypeDescriptor<ReadableDuration>() {};
private static final VarLongCoder LONG_CODER = VarLongCoder.of();
private DurationCoder() {}
private Long toLong(ReadableDuration value) {
return value.getMillis();
}
private ReadableDuration fromLong(Long decoded) {
return Duration.millis(decoded);
}
@Override
public void encode(ReadableDuration value, OutputStream outStream)
throws CoderException, IOException {
if (value == null) {
throw new CoderException("cannot encode a null ReadableDuration");
}
LONG_CODER.encode(toLong(value), outStream);
}
@Override
public ReadableDuration decode(InputStream inStream) throws CoderException, IOException {
return fromLong(LONG_CODER.decode(inStream));
}
@Override
public void verifyDeterministic() {
LONG_CODER.verifyDeterministic();
}
/**
* {@inheritDoc}
*
* @return {@code true}. This coder is injective.View on GitHub (pinned to 12126d8942)
Solutions
- Filter or default null durations before the encoding point (e.g. Duration.ZERO or skip).
- Wrap with NullableCoder.of(DurationCoder.of()) for nullable duration collections.
- Fix the producing transform to return a non-null duration (e.g. use Duration.millis(0) as sentinel).
- Change the element type to Optional<Duration> with a supporting coder.
Example fix
// before .apply(MapElements.into(TypeDescriptor.of(ReadableDuration.class)).via(e -> e.getEnd() != null ? new Duration(e.getStart(), e.getEnd()) : null)) // after .apply(Filter.by(e -> e.getEnd() != null)) .apply(MapElements.into(TypeDescriptor.of(ReadableDuration.class)).via(e -> new Duration(e.getStart(), e.getEnd())))
Defensive patterns
Strategy: type-guard
Validate before calling
if (duration == null) { duration = Duration.ZERO; /* or skip record */ } Type guard
boolean isEncodable(ReadableDuration d) { return d != null; } Try / catch
try {
pipeline.run().waitUntilFinish();
} catch (RuntimeException e) {
if (e.getMessage() != null && e.getMessage().contains("null ReadableDuration")) {
// add null filtering or NullableCoder.of(DurationCoder.of())
}
throw e;
} Prevention
- Compute durations defensively; default missing endpoints to Duration.ZERO or drop records.
- Wrap with NullableCoder.of when null durations are valid domain values.
When it happens
Trigger: Encoding a PCollection containing a null org.joda.time.ReadableDuration (e.g. from a MapElements that returns null duration), when the resolved coder is DurationCoder.
Common situations: Computing durations between events where one side is missing; mapping Option/nullable fields directly to Duration; Kafka/parse pipelines producing null gaps.
Related errors
- cannot encode a null Byte
- cannot estimate size for unsupported null value
- cannot encode a null Double
- cannot encode a null Float
- cannot encode a null Map
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/eac1ce231669911d.
Report an issue: GitHub.