apache/beam · error · IllegalStateException
Attempting to emit an element outside of a @ProcessElement c
Error message
Attempting to emit an element outside of a @ProcessElement context.
What it means
Thrown as IllegalStateException when SingleOutputReceiver.output is invoked while currentElement is null, i.e. emission is attempted outside @ProcessElement (or @StartBundle/@FinishBundle contexts that don't have an element). Outputting the main output requires an active current element whose metadata (timestamp, window, pane) can be reused.
Source
Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java:1879
private class NonWindowObservingProcessBundleContext
extends NonWindowObservingProcessBundleContextBase {
@Override
public OutputBuilder<OutputT> builder(OutputT value) {
return WindowedValues.builder(currentElement)
.withValue(value)
.setReceiver(
windowedValue -> {
checkTimestamp(windowedValue.getTimestamp());
outputTo(mainOutputConsumer, windowedValue);
});
}
@Override
public void output(OutputT output) {
// Don't need to check timestamp since we can always output using the input timestamp.
if (currentElement == null) {
throw new IllegalStateException(
"Attempting to emit an element outside of a @ProcessElement context.");
}
outputTo(mainOutputConsumer, currentElement.withValue(output));
}
@Override
public <T> void output(TupleTag<T> tag, T output) {
FnDataReceiver<WindowedValue<T>> consumer =
(FnDataReceiver) localNameToConsumer.get(tag.getId());
if (consumer == null) {
throw new IllegalArgumentException(String.format("Unknown output tag %s", tag));
}
// Don't need to check timestamp since we can always output using the input timestamp.
outputTo(consumer, currentElement.withValue(output));
}
@Override
public void outputWithTimestamp(OutputT output, Instant timestamp) {View on GitHub (pinned to 12126d8942)
Solutions
- Only call output() within @ProcessElement; from @OnTimer use the timer context or timers to emit side effects.
- Buffer elements during @ProcessElement and emit them in the same call; use @FinishBundle with its own FinishBundleContext.output API instead of the element context.
- Capture the value (not the context) if you must emit from async code, then emit synchronously before ProcessElement returns (or use runner-supported async output APIs).
- Add an assertion/guard that currentElement != null in helper methods that call output.
Example fix
// before
@OnTimer("t")
public void onTimer(OnTimerContext ctx) { ctx.output(result); } // wrong context usage pattern
// after: emit during @ProcessElement or buffer and use bundle context
@FinishBundle
public void finish(FinishBundleContext ctx) { ctx.output(MAIN_TAG, bufferedResult, Instant.now(), window); } Defensive patterns
Strategy: validation
Validate before calling
if (context == null || !inProcessElement) {
throw new IllegalStateException("output() called outside @ProcessElement");
} Type guard
boolean canEmit(ProcessContext c) { return c != null && currentElement() != null; } Try / catch
try {
c.output(value);
} catch (IllegalStateException e) {
logger.error("Emission attempted outside @ProcessElement; buffer for FinishBundle instead", e);
} Prevention
- Never store ProcessContext in a field for later use
- Emit from onTimer using timer context APIs, not element output
- Do async work inside ProcessElement and emit before returning
When it happens
Trigger: Calling c.output(value) from a timer callback (onTimer), from @StartBundle/@FinishBundle, or from an async thread after @ProcessElement returned; storing the ProcessContext in a field and calling output later.
Common situations: Emitting from onTimerContext using the element-context API instead of Timer output; leaking context into ExecutorService callbacks; FinishBundle code that reuses the ProcessContext captured during ProcessElement.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot access StartBundleContext outside of @StartBundle met
- Cannot access FinishBundleContext outside of @FinishBundle m
- SolaceIO.Write.UnboundedSolaceWriter.Context: No context pro
- Cannot be called outside of a DoFn's process method.
- State stream is closed.
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/84bcee1d5bcf2b9d.
Report an issue: GitHub.