{"record":{"id":"869d69b34a65a6e7","repo":"apache/beam","slug":"cannot-access-time-domain-outside-of-processtimer-method","errorCode":null,"errorMessage":"Cannot access time domain outside of @ProcessTimer method.","messagePattern":"Cannot access time domain outside of @ProcessTimer method\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java","lineNumber":2078,"sourceCode":"    public @Nullable Long currentRecordOffset(DoFn<InputT, OutputT> doFn) {\n      return currentRecordOffset();\n    }\n\n    @Override\n    public Instant fireTimestamp(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          \"Cannot access fire timestamp outside of @OnTimer method.\");\n    }\n\n    @Override\n    public String timerId(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          \"Cannot access timerId as parameter outside of @OnTimer method.\");\n    }\n\n    @Override\n    public TimeDomain timeDomain(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          \"Cannot access time domain outside of @ProcessTimer method.\");\n    }\n\n    @Override\n    public OutputReceiver<OutputT> outputReceiver(DoFn<InputT, OutputT> doFn) {\n      return this;\n    }\n\n    @Override\n    // OutputT == RestrictionT\n    public void output(OutputT output) {\n      OutputReceiver.super.output(output);\n    }\n\n    private final OutputReceiver<Row> mainRowOutputReceiver =\n        mainOutputSchemaCoder == null\n            ? null\n            : new OutputReceiver<Row>() {","sourceCodeStart":2060,"sourceCodeEnd":2096,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java#L2060-L2096","documentation":"FnApiDoFnRunner.timeDomain() returns the TimeDomain (event time vs processing time) of the currently firing timer. Because timer firing is delivered via the runner's @ProcessTimer handling, this value only exists inside a timer callback; the accessor throws UnsupportedOperationException everywhere else. The message references @ProcessTimer, the internal runner entry point for fired timers (user code sees this as @OnTimer).","triggerScenarios":"Calling context.timeDomain() (or binding a TimeDomain parameter) from @ProcessElement/@StartBundle/@FinishBundle instead of a @OnTimer method; timer-family parameter resolution wiring TimeDomain into a non-timer invocation inside the Fn API harness.","commonSituations":"Code migrated from runners where TimeDomain was accessible more broadly; generic context helper methods that read timeDomain unconditionally; mixing event-time and processing-time timer handling and probing the domain from the wrong callback.","solutions":["Access timeDomain() only from inside a @OnTimer method (OnTimerContext).","If you need domain-dependent behavior for elements, decide it explicitly in your pipeline logic (you already chose the domain when setting the timer via Timer.withTimeDomain / in event-time vs processing-time timer creation).","Split element-path and timer-path code so timer-only context parameters (TimeDomain, TimerId, fireTimestamp) are never reachable from the element path.","Update helpers to take TimeDomain as an explicit parameter from the @OnTimer method."],"exampleFix":"// before\n@ProcessElement\npublic void processElement(ProcessContext c) {\n  TimeDomain d = c.timeDomain(); // throws\n}\n// after\n@OnTimer(\"tick\")\npublic void onTimer(OnTimerContext c) {\n  TimeDomain d = c.timeDomain(); // valid here\n}","handlingStrategy":"validation","validationCode":"TimeDomain domain = null;\nif (c instanceof DoFn.OnTimerContext) {\n  domain = ((DoFn.OnTimerContext) c).timeDomain();\n} else {\n  // element path: decide domain from your own timer configuration\n  domain = myTimerIsProcessingTime ? TimeDomain.PROCESSING_TIME : TimeDomain.EVENT_TIME;\n}","typeGuard":"static boolean canReadTimeDomain(DoFn.ProcessContext c) {\n  return c instanceof DoFn.OnTimerContext;\n}","tryCatchPattern":"try {\n  TimeDomain d = ((DoFn.OnTimerContext) c).timeDomain();\n} catch (UnsupportedOperationException e) {\n  TimeDomain d = configuredDomain; // from Timer.withTimeDomain choice\n}","preventionTips":["Read timeDomain() only inside @OnTimer","You already picked the domain when creating the timer; store it if needed elsewhere","Split event-time and processing-time timer handlers into separate methods","Pin Beam version and check DoFn parameter-binding docs for the harness in use"],"tags":["java","apache-beam","timers","time-domain","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}