apache/beam · error · IllegalStateException

Timers are unsupported because the ProcessBundleRequest %s d

Error message

Timers are unsupported because the ProcessBundleRequest %s does not provide a timer ApiServiceDescriptor.

What it means

The pipeline contains timers, so the SDK harness tries to register an outgoing timers endpoint for the timer family. But the ProcessBundleRequest sent by the runner did not include a timer ApiServiceDescriptor, meaning the runner provided no gRPC endpoint to deliver timers to. BeamFnLoggingClient's sibling ProcessBundleHandler throws IllegalStateException in addOutgoingTimersEndpoint.

Source

Thrown at sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ProcessBundleHandler.java:392

                    org.apache.beam.sdk.coders.Coder<T> coder) {
                  BeamFnDataOutboundAggregator aggregator =
                      outboundAggregatorMap.computeIfAbsent(
                          apiServiceDescriptor,
                          asd ->
                              new BeamFnDataOutboundAggregator(
                                  options,
                                  runnerCapabilities.contains(
                                      BeamUrns.getUrn(
                                          StandardRunnerProtocols.Enum
                                              .CONTROL_RESPONSE_ELEMENTS_EMBEDDING))));
                  return aggregator.registerOutputDataLocation(pTransformId, coder);
                }

                @Override
                public <T> FnDataReceiver<Timer<T>> addOutgoingTimersEndpoint(
                    String timerFamilyId, org.apache.beam.sdk.coders.Coder<Timer<T>> coder) {
                  if (!processBundleDescriptor.hasTimerApiServiceDescriptor()) {
                    throw new IllegalStateException(
                        String.format(
                            "Timers are unsupported because the ProcessBundleRequest %s does not"
                                + " provide a timer ApiServiceDescriptor.",
                            processBundleInstructionId.get()));
                  }
                  BeamFnDataOutboundAggregator aggregator =
                      outboundAggregatorMap.computeIfAbsent(
                          processBundleDescriptor.getTimerApiServiceDescriptor(),
                          asd ->
                              new BeamFnDataOutboundAggregator(
                                  options,
                                  runnerCapabilities.contains(
                                      BeamUrns.getUrn(
                                          StandardRunnerProtocols.Enum
                                              .CONTROL_RESPONSE_ELEMENTS_EMBEDDING))));
                  return aggregator.registerOutputTimersLocation(
                      pTransformId, timerFamilyId, coder);
                }

View on GitHub (pinned to 12126d8942)

Solutions

  1. Use a runner/backend that supports timers over the Fn API and ensure it sends the timer ApiServiceDescriptor.
  2. Upgrade runner and SDK harness to matching versions where timers are supported.
  3. Remove @OnTimer usage (or the whole stateful DoFn) if targeting a backend without timer support.
  4. Inspect ProcessBundleRequest/instruction logs to confirm timerApiServiceDescriptor is populated by the runner.

Example fix

// before
// runner config without timer support running stateful DoFn
// after
// upgrade runner, e.g. use a Flink/Beam version that sends timerApiServiceDescriptor, or drop @OnTimer
Defensive patterns

Strategy: validation

Validate before calling

// before submitting: if (doFnUsesTimers && !runnerSupportsTimers(backendVersion)) throw new IllegalArgumentException("Backend lacks timer support");

Try / catch

try { startBundleWithTimers(); } catch (IllegalStateException e) { if (e.getMessage().contains("does not provide a timer ApiServiceDescriptor")) { /* fail fast / switch runner */ } throw e; }

Prevention

When it happens

Trigger: A DoFn with @OnTimer / state timers runs in a bundle whose ProcessBundleRequest lacks timerApiServiceDescriptor — typically a runner/portability backend that does not support timers, or a misconfigured job where the instruction's API service descriptors omit timers.

Common situations: Running a stateful DoFn on a portability runner/backend without timer support (older Flink/Spark portable versions, custom runners, direct harness misconfig); runner/harness version mismatch where the runner doesn't send the timer endpoint.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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