apache/beam · error · IllegalStateException

Outbound data endpoint already registered for

Error message

Outbound data endpoint already registered for 

What it means

BeamFnDataOutboundAggregator registers one outbound logical data endpoint per pTransformId per bundle. This IllegalStateException is thrown when registerOutputDataLocation is called with a pTransformId that already has a registered data receiver, preventing duplicate endpoint registration which would corrupt the bundle's output mapping.

Source

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

  public void start() {
    if (timeLimit > 0 && this.flushFuture == null) {
      this.flushFuture =
          Executors.newSingleThreadScheduledExecutor(
                  new ThreadFactoryBuilder()
                      .setDaemon(true)
                      .setNameFormat("DataBufferOutboundFlusher-thread")
                      .build())
              .scheduleAtFixedRate(this::flush, timeLimit, timeLimit, TimeUnit.MILLISECONDS);
    }
  }

  /**
   * Register the outbound data logical endpoint, returns the FnDataReceiver for processing the
   * endpoint's outbound data.
   */
  public <T> FnDataReceiver<T> registerOutputDataLocation(String pTransformId, Coder<T> coder) {
    if (outputDataReceivers.containsKey(pTransformId)) {
      throw new IllegalStateException(
          "Outbound data endpoint already registered for " + pTransformId);
    }
    Receiver<T> receiver = new Receiver<>(coder);
    if (timeLimit > 0) {
      outputDataReceivers.put(pTransformId, receiver);
      return data -> {
        checkFlushThreadException();
        synchronized (flushLock) {
          receiver.accept(data);
        }
      };
    }
    outputDataReceivers.put(pTransformId, receiver);
    return receiver;
  }

  /**
   * Register the outbound timers logical endpoint, returns the FnDataReceiver for processing the

View on GitHub (pinned to 12126d8942)

Solutions

  1. Ensure registerOutputDataLocation is called at most once per pTransformId per bundle; check existing registrations before calling
  2. Check that the wiring code (addOutgoingDataEndpoint, registerOutputLocation, newBundle) is not invoked redundantly
  3. Upgrade Beam — bundle lifecycle bugs in custom runners have been fixed in later versions
  4. Recreate/reset the aggregator between bundles so stale registrations are cleared

Example fix

// before
aggregator.registerOutputDataLocation(pTransformId, coder);
aggregator.registerOutputDataLocation(pTransformId, coder); // throws
// after
// register exactly once per pTransformId per bundle
Defensive patterns

Strategy: validation

Validate before calling

if (registeredTransformIds.contains(pTransformId)) {
  throw new IllegalArgumentException("duplicate data endpoint " + pTransformId);
}
registeredTransformIds.add(pTransformId);

Try / catch

try {
  aggregator.registerOutputDataLocation(pTransformId, coder);
} catch (IllegalStateException e) {
  LOG.warn("endpoint already registered: " + pTransformId, e);
}

Prevention

When it happens

Trigger: Calling registerOutputDataLocation twice with the same pTransformId within one bundle; transform/receiver wiring (e.g. via addOutgoingDataEndpoint or fnDataReceiver) registering the same output location twice.

Common situations: Runner/SDK harness bugs where bundle setup runs the registration path twice (a PTransform re-initialized mid-bundle); custom runner code calling registerOutputLocation without clearing prior state between bundles.

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