{"record":{"id":"90c099e5676d3835","repo":"apache/beam","slug":"the-on-timer-callback-for-s-is-not-the-specified-s-method","errorCode":null,"errorMessage":"The on_timer callback for %s is not the specified .%s method for DoFn %r (perhaps it was overwritten?).","messagePattern":"The on_timer callback for (.+?) is not the specified \\.(.+?) method for DoFn %r \\(perhaps it was overwritten\\?\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/transforms/userstate.py","lineNumber":319,"sourceCode":"  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\n\n_TimerTuple = collections.namedtuple('timer_tuple', ('cleared', 'timestamp'))  # type: ignore[name-match]\n\n\nclass RuntimeTimer(BaseTimer):\n  \"\"\"Timer interface object passed to user code.\"\"\"","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/transforms/userstate.py#L301-L337","documentation":"Apache Beam raises this ValueError when the TimerSpec's attached on_timer callback does not match the method of the same name resolved on the DoFn instance (getattr(dofn, method_name).__func__). This indicates the declared callback was overwritten, rebound, or is a different function than the one living on the class. The check runs in validate_stateful_dofn to catch subtle wiring bugs where a timer would silently fire the wrong handler.","triggerScenarios":"Assigning over the on_timer method after TimerSpec creation (e.g. subclass overrides, monkey-patching, or decorating the method after binding so the bound __func__ differs from the one captured in _attached_callback); validate_stateful_dofn compares timer_spec._attached_callback != getattr(dofn, method_name, None).__func__ and fails.","commonSituations":"Subclassing a DoFn and overriding the timer callback; applying decorators (e.g. wrappers) that replace the original function; dynamic attribute assignment on the DoFn class; copying a TimerSpec from another class where the callback belongs elsewhere.","solutions":["Ensure the TimerSpec references the exact method defined on this DoFn class and that it is not overwritten later.","If subclassing, redefine the TimerSpec in the subclass pointing at the subclass's callback.","Remove decorators/monkey-patches that replace the bound on_timer function, or recreate the TimerSpec after the final method definition."],"exampleFix":"// before\nclass MyDoFn(DoFn):\n  T = TimerSpec('t', on_timer=on_timer)\n  def on_timer(self): ...\nMyDoFn.on_timer = lambda self: None  # overwrites -> mismatch\n// after\nclass MyDoFn(DoFn):\n  T = TimerSpec('t', on_timer=on_timer)\n  def on_timer(self): ...  # never reassigned","handlingStrategy":"validation","validationCode":"from apache_beam.transforms.userstate import get_dofn_specs\nfor t in get_dofn_specs(MyDoFn)[1]:\n  cb = t._attached_callback\n  assert cb == getattr(MyDoFn, cb.__name__, None), f'{t.name} callback not the class method'","typeGuard":null,"tryCatchPattern":"try:\n  validate_stateful_dofn(MyDoFn)\nexcept ValueError as e:\n  if 'perhaps it was overwritten' in str(e):\n    inspect subclass overrides of the timer callback\n  else:\n    raise","preventionTips":["Do not reassign or monkey-patch on_timer methods after TimerSpec declaration.","In subclasses, redefine the TimerSpec to point at the subclass callback.","Avoid decorating timer callbacks with wrappers that replace the function object."],"tags":["python","apache-beam","timers","callback-mismatch","validation"],"backgroundTag":"internal-invariant-violation","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"}