{"record":{"id":"fa20d44aaca4fde4","repo":"apache/beam","slug":"dofn-r-has-a-timerspec-without-an-associated-on-timer","errorCode":null,"errorMessage":"DoFn %r has a TimerSpec without an associated on_timer callback: %s.","messagePattern":"DoFn %r has a TimerSpec without an associated on_timer callback: (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/transforms/userstate.py","lineNumber":313,"sourceCode":"  \"\"\"Validates the proper specification of a stateful DoFn.\"\"\"\n\n  # Get state and timer specs.\n  all_state_specs, all_timer_specs = get_dofn_specs(dofn)\n\n  # Reject DoFns that have multiple state or timer specs with the same name.\n  if len(all_state_specs) != len(set(s.name for s in all_state_specs)):\n    raise ValueError(\n        'DoFn %r has multiple StateSpecs with the same name: %s.' %\n        (dofn, all_state_specs))\n  if len(all_timer_specs) != len(set(s.name for s in all_timer_specs)):\n    raise ValueError(\n        'DoFn %r has multiple TimerSpecs with the same name: %s.' %\n        (dofn, all_timer_specs))\n\n  # Reject DoFns that use timer specs without corresponding timer callbacks.\n  for timer_spec in all_timer_specs:\n    if not timer_spec._attached_callback:\n      raise ValueError((\n          'DoFn %r has a TimerSpec without an associated on_timer '\n          'callback: %s.') % (dofn, timer_spec))\n    method_name = timer_spec._attached_callback.__name__\n    if (timer_spec._attached_callback != getattr(dofn, method_name,\n                                                 None).__func__):  # type: ignore[union-attr]\n      raise ValueError((\n          'The on_timer callback for %s is not the specified .%s method '\n          'for DoFn %r (perhaps it was overwritten?).') %\n                       (timer_spec, method_name, dofn))\n\n\nclass BaseTimer(object):\n  def clear(self, dynamic_timer_tag: str = '') -> None:\n    raise NotImplementedError\n\n  def set(self, timestamp: Timestamp, dynamic_timer_tag: str = '') -> None:\n    raise NotImplementedError\n","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/transforms/userstate.py#L295-L331","documentation":"Apache Beam raises this ValueError when a DoFn declares a TimerSpec whose _attached_callback is empty/None, meaning no on_timer callback was wired to the timer. A timer with no callback can never be handled when it fires, so the pipeline is rejected during validate_stateful_dofn. This usually means the TimerSpec was created without the on_timer= argument (or with a misspelled callback reference that didn't bind).","triggerScenarios":"Declaring TimerSpec('name') without on_timer=, or passing a name instead of the method object so the decorator never attaches a callback; validate_stateful_dofn then finds timer_spec._attached_callback falsy.","commonSituations":"Typo in the callback parameter name; writing the timer spec before defining the on_timer method; porting code where on_timer was accidentally dropped; using a lambda the mechanism doesn't accept.","solutions":["Pass the on_timer= method to TimerSpec: TimerSpec('name', on_timer=MyDoFn.process_timer).","Fix typos in the callback reference so the timer's _attached_callback is set.","Remove the TimerSpec entirely if the timer is no longer used."],"exampleFix":"// before\nEXPIRY_TIMER = TimerSpec('expiry')  # no on_timer\n// after\nEXPIRY_TIMER = TimerSpec('expiry', on_timer=process_expiry)","handlingStrategy":"validation","validationCode":"from apache_beam.transforms.userstate import get_dofn_specs\nfor t in get_dofn_specs(MyDoFn)[1]:\n  assert t._attached_callback, f'TimerSpec {t.name} has no on_timer callback'","typeGuard":null,"tryCatchPattern":"try:\n  validate_stateful_dofn(MyDoFn)\nexcept ValueError as e:\n  if 'TimerSpec without an associated on_timer' in str(e):\n    attach_timer_callbacks(MyDoFn)\n  else:\n    raise","preventionTips":["Always pass on_timer=MyDoFn.method when creating a TimerSpec.","Watch for typos between the timer name and callback method name.","Add a unit test calling validate_stateful_dofn on every stateful DoFn."],"tags":["python","apache-beam","timers","missing-callback","validation"],"backgroundTag":"missing-required-argument","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"}