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

  1. Only call output() within @ProcessElement; from @OnTimer use the timer context or timers to emit side effects.
  2. Buffer elements during @ProcessElement and emit them in the same call; use @FinishBundle with its own FinishBundleContext.output API instead of the element context.
  3. 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).
  4. 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

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


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