{"record":{"id":"38222f5f23c5331a","repo":"apache/beam","slug":"cannot-access-key-as-parameter-outside-of-ontimer-method-38222f","errorCode":null,"errorMessage":"Cannot access key as parameter outside of @OnTimer method.","messagePattern":"Cannot access key as parameter outside of @OnTimer method\\.","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java","lineNumber":318,"sourceCode":"   */\n  @Internal\n  abstract class BaseArgumentProvider<InputT, OutputT>\n      implements ArgumentProvider<InputT, OutputT> {\n    @Override\n    public DoFn<InputT, OutputT>.ProcessContext processContext(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"ProcessContext unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public InputT element(DoFn<InputT, OutputT> doFn) {\n      throw new UnsupportedOperationException(\n          String.format(\"Element unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public @Nullable Object key() {\n      throw new UnsupportedOperationException(\n          \"Cannot access key as parameter outside of @OnTimer method.\");\n    }\n\n    @Override\n    public @Nullable Object sideInput(String tagId) {\n      throw new UnsupportedOperationException(\n          String.format(\"SideInput unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public TimerMap timerFamily(String tagId) {\n      throw new UnsupportedOperationException(\n          String.format(\"TimerFamily unsupported in %s\", getErrorContext()));\n    }\n\n    @Override\n    public @Nullable Object schemaElement(int index) {\n      throw new UnsupportedOperationException(","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/transforms/reflect/DoFnInvoker.java#L300-L336","documentation":"The restartable-key parameter (Object key()) of an ArgumentProvider is only available inside @OnTimer methods, where Beam knows the timer's key. The default BaseArgumentProvider.key() always throws UnsupportedOperationException with this message because a key cannot be determined outside that context.","triggerScenarios":"Declaring a RestartableHandler-style method (or calling key()) from a @ProcessElement, @StartBundle, or @FinishBundle method whose ArgumentProvider only supports non-timer contexts; directly invoking key() on a default BaseArgumentProvider in tests.","commonSituations":"Confusing RestartableHandler.key() semantics by adding key parameters to @ProcessElement methods; tests exercising timer-adjacent code with a bare BaseArgumentProvider; custom runner code that invokes key() without a timer context.","solutions":["Restrict key() access (and RestartableHandler usage) to @OnTimer methods","Remove the key parameter from non-timer DoFn methods — only @OnTimer/RestartableHandler methods may receive it","In tests, override key() in a custom ArgumentProvider returning the expected key","If in runner code, install a timer-aware ArgumentProvider when invoking @OnTimer methods"],"exampleFix":"// before\n@ProcessElement\npublic void processElement(ProcessContext c, Object key) { ... }\n// after\n@OnTimer(\"myTimer\")\npublic void onTimer(OnTimerContext c) {\n  // key context only valid inside @OnTimer\n}","handlingStrategy":"validation","validationCode":"// key() is only valid for @OnTimer methods:\nboolean isOnTimer = method.isAnnotationPresent(DoFn.OnTimer.class)\n    || method.getDeclaringClass() == DoFn.RestartableHandler.class;\nif (!isOnTimer) {\n  throw new IllegalStateException(\"key parameter only allowed in @OnTimer methods\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  Object key = args.key();\n} catch (UnsupportedOperationException e) {\n  throw new IllegalStateException(\"key is only accessible inside @OnTimer methods\", e);\n}","preventionTips":["Only use RestartableHandler/key parameters in @OnTimer methods","Remove key parameters from @ProcessElement — use stateful DoFns keyed by KV keys instead","Test timer code with a custom ArgumentProvider overriding key()","Consult DoFnSignatures validation output at pipeline-construction time"],"tags":["java","unsupported-operation","dofn","timers"],"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"}