{"record":{"id":"b58c43ed09f77a72","repo":"apache/beam","slug":"timers-are-unsupported-because-the-processbundlerequest-s","errorCode":null,"errorMessage":"Timers are unsupported because the ProcessBundleRequest %s does not provide a timer ApiServiceDescriptor.","messagePattern":"Timers are unsupported because the ProcessBundleRequest (.+?) does not provide a timer ApiServiceDescriptor\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ProcessBundleHandler.java","lineNumber":392,"sourceCode":"                    org.apache.beam.sdk.coders.Coder<T> coder) {\n                  BeamFnDataOutboundAggregator aggregator =\n                      outboundAggregatorMap.computeIfAbsent(\n                          apiServiceDescriptor,\n                          asd ->\n                              new BeamFnDataOutboundAggregator(\n                                  options,\n                                  runnerCapabilities.contains(\n                                      BeamUrns.getUrn(\n                                          StandardRunnerProtocols.Enum\n                                              .CONTROL_RESPONSE_ELEMENTS_EMBEDDING))));\n                  return aggregator.registerOutputDataLocation(pTransformId, coder);\n                }\n\n                @Override\n                public <T> FnDataReceiver<Timer<T>> addOutgoingTimersEndpoint(\n                    String timerFamilyId, org.apache.beam.sdk.coders.Coder<Timer<T>> coder) {\n                  if (!processBundleDescriptor.hasTimerApiServiceDescriptor()) {\n                    throw new IllegalStateException(\n                        String.format(\n                            \"Timers are unsupported because the ProcessBundleRequest %s does not\"\n                                + \" provide a timer ApiServiceDescriptor.\",\n                            processBundleInstructionId.get()));\n                  }\n                  BeamFnDataOutboundAggregator aggregator =\n                      outboundAggregatorMap.computeIfAbsent(\n                          processBundleDescriptor.getTimerApiServiceDescriptor(),\n                          asd ->\n                              new BeamFnDataOutboundAggregator(\n                                  options,\n                                  runnerCapabilities.contains(\n                                      BeamUrns.getUrn(\n                                          StandardRunnerProtocols.Enum\n                                              .CONTROL_RESPONSE_ELEMENTS_EMBEDDING))));\n                  return aggregator.registerOutputTimersLocation(\n                      pTransformId, timerFamilyId, coder);\n                }","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/control/ProcessBundleHandler.java#L374-L410","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a runner/backend that supports timers over the Fn API and ensure it sends the timer ApiServiceDescriptor.","Upgrade runner and SDK harness to matching versions where timers are supported.","Remove @OnTimer usage (or the whole stateful DoFn) if targeting a backend without timer support.","Inspect ProcessBundleRequest/instruction logs to confirm timerApiServiceDescriptor is populated by the runner."],"exampleFix":"// before\n// runner config without timer support running stateful DoFn\n// after\n// upgrade runner, e.g. use a Flink/Beam version that sends timerApiServiceDescriptor, or drop @OnTimer\n","handlingStrategy":"validation","validationCode":"// before submitting: if (doFnUsesTimers && !runnerSupportsTimers(backendVersion)) throw new IllegalArgumentException(\"Backend lacks timer support\");","typeGuard":null,"tryCatchPattern":"try { startBundleWithTimers(); } catch (IllegalStateException e) { if (e.getMessage().contains(\"does not provide a timer ApiServiceDescriptor\")) { /* fail fast / switch runner */ } throw e; }","preventionTips":["Confirm the runner/backend supports Fn API timers before using @OnTimer","Keep runner and SDK harness versions in lockstep","Remove timer usage for unsupported backends"],"tags":["apache-beam","java-harness","timers","portability"],"backgroundTag":"missing-required-config-field","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}