apache/beam · info · PoisonedException

PoisonedException

Error message

PoisonedException

What it means

While inserting a placeholder future in forwardToConsumerForInstructionId, the computeIfAbsent callback detects the instruction id was poisoned and throws PoisonedException (a sentinel exception). onNext uses this to discard inbound data for an instruction that previously failed, treating it as ignorable rather than a data-plane error.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/fn/data/BeamFnDataGrpcMultiplexer.java:274

        }
        for (BeamFnApi.Elements.Timers timers : value.getTimersList()) {
          if (instructionId.equals(timers.getInstructionId())) {
            builder.addTimers(timers);
          }
        }
        forwardToConsumerForInstructionId(instructionId, builder.build());
      }
    }

    private void forwardToConsumerForInstructionId(String instructionId, BeamFnApi.Elements value) {
      CloseableFnDataReceiver<BeamFnApi.Elements> consumer;
      try {
        CompletableFuture<CloseableFnDataReceiver<BeamFnApi.Elements>> consumerFuture =
            receivers.computeIfAbsent(
                instructionId,
                (unused) -> {
                  if (poisonedInstructionIds.getIfPresent(instructionId) != null) {
                    throw new PoisonedException();
                  }
                  LOG.debug(
                      "Received data for instruction {} without consumer ready. "
                          + "Waiting for consumer to be registered.",
                      instructionId);
                  return new CompletableFuture<>();
                });
        // The consumer may not be registered until the bundle processor is fully constructed so we
        // conservatively set
        // a high timeout.  Poisoning will prevent this for occurring for consumers that will not be
        // registered.
        consumer = consumerFuture.get(3, TimeUnit.HOURS);
        /*
         * TODO: On failure we should fail any bundles that were impacted eagerly
         * instead of relying on the Runner harness to do all the failure handling.
         */
      } catch (TimeoutException e) {
        LOG.error(

View on GitHub (pinned to 12126d8942)

Solutions

  1. No user action usually needed: PoisonedException is caught internally and the data is ignored by design.
  2. If seen repeatedly, check for SDK/runner version mismatches causing bundles to be failed while data is still in flight.
  3. Ensure process-bundle failures are communicated promptly (fail bundle) so the harness stops streaming data for that instruction.
Defensive patterns

Strategy: try-catch

Try / catch

try { multiplexer.onNext(elements); } catch (PoisonedException e) { /* expected: ignore data for failed instruction */ }

Prevention

When it happens

Trigger: gRPC inbound onNext delivers BeamFnApi.Elements for an instruction id that was poisoned via poison(instructionId) after a processing failure, and no receiver future exists for it.

Common situations: SDK harness sends trailing/last elements for a bundle the runner already failed; network-level retries redeliver data for an aborted instruction.

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/d2f7a439804c810a. Report an issue: GitHub.