{"record":{"id":"58c80e3845021cf0","repo":"apache/beam","slug":"cannot-access-timerid-as-parameter-outside-of-ontimer-method-58c80e","errorCode":null,"errorMessage":"Cannot access timerId as parameter outside of @OnTimer method.","messagePattern":"Cannot access timerId as parameter outside of @OnTimer method\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java","lineNumber":2072,"sourceCode":"    @Override\n    public @Nullable String currentRecordId(DoFn<InputT, OutputT> doFn) {\n      return currentRecordId();\n    }\n\n    @Override\n    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);","sourceCodeStart":2054,"sourceCodeEnd":2090,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/harness/src/main/java/org/apache/beam/fn/harness/FnApiDoFnRunner.java#L2054-L2090","documentation":"FnApiDoFnRunner.timerId() backs the timer-id parameter exposed to @OnTimer methods (TimerId param / context.timerId()). The runner only records the active timer id while dispatching a fired timer; outside that window there is no timer id to return, so it throws UnsupportedOperationException unconditionally. It is a misuse guard, not an internal failure.","triggerScenarios":"Calling context.timerId() (or declaring a TimerId/String timer-id parameter) from @ProcessElement, @StartBundle, @FinishBundle, or a watermark-expiration path rather than from a @OnTimer-annotated method. Any DoFn signature resolution that binds timerId for a non-timer invocation.","commonSituations":"Refactoring @OnTimer logic into shared helpers invoked from both element and timer paths; declaring a TimerId parameter on the wrong method; copying examples where the annotation was dropped during editing, so the harness treats the method as element processing.","solutions":["Move timerId() access (or the TimerId parameter) into the @OnTimer-annotated method only.","Pass the timer id explicitly as a plain argument when shared helper code needs it from a timer callback.","If the timer name is needed statically for setup, hardcode/derive the id constant used in the Timer.withTimeDomain(...)/Timer.of call instead of reading it from the context.","Check that the method containing the access carries @OnTimer and the correct timer family/id."],"exampleFix":"// before\n@ProcessElement\npublic void processElement(ProcessContext c) {\n  String id = c.timerId(); // throws\n}\n// after\n@OnTimer(\"cleanup\")\npublic void onTimer(OnTimerContext c) {\n  String id = c.timerId(); // valid only here\n}","handlingStrategy":"validation","validationCode":"// Ensure the accessor is only called from a method annotated @OnTimer\nMethod m = /* currently executing method */;\nif (m == null || !m.isAnnotationPresent(OnTimer.class)) {\n  throw new IllegalStateException(\"timerId is only available inside @OnTimer\");\n}","typeGuard":"static boolean canReadTimerId(DoFn.ProcessContext c) {\n  return c instanceof DoFn.OnTimerContext;\n}","tryCatchPattern":"try {\n  String id = ((DoFn.OnTimerContext) c).timerId();\n} catch (UnsupportedOperationException e) {\n  // outside @OnTimer: use the static timer id string instead\n  String id = \"cleanup\";\n}","preventionTips":["Declare TimerId parameters only on @OnTimer methods","Never share a single context helper across element and timer paths","Pass the timer id explicitly to shared helpers from the @OnTimer method","Review DoFn annotations after refactors; the harness binds params by annotation"],"tags":["java","apache-beam","timers","unsupported-operation","dofn"],"backgroundTag":"unsupported-operation","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"}