apache/beam · error · ValueError
The on_timer callback for
Error message
The on_timer callback for %s is not the specified .%s method for DoFn %r (perhaps it was overwritten?).
What it means
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.
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.
Example fix
// before
class MyDoFn(DoFn):
T = TimerSpec('t', on_timer=on_timer)
def on_timer(self): ...
MyDoFn.on_timer = lambda self: None # overwrites -> mismatch
// after
class MyDoFn(DoFn):
T = TimerSpec('t', on_timer=on_timer)
def on_timer(self): ... # never reassigned Defensive patterns
Strategy: validation
Validate before calling
from apache_beam.transforms.userstate import get_dofn_specs
for t in get_dofn_specs(MyDoFn)[1]:
cb = t._attached_callback
assert cb == getattr(MyDoFn, cb.__name__, None), f'{t.name} callback not the class method' Try / catch
try:
validate_stateful_dofn(MyDoFn)
except ValueError as e:
if 'perhaps it was overwritten' in str(e):
inspect subclass overrides of the timer callback
else:
raise Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- DoFn %r has a TimerSpec without an associated on_timer…
- DoFn %r has multiple TimerSpecs with the same name
- An unsupported sink was specified
- At least one of --render_port or --render_output must be…
- buffer_sec must be >= 0, got
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/90c099e5676d3835.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/python/apache_beam/transforms/userstate.py:319
if len(all_state_specs) != len(set(s.name for s in all_state_specs)):
raise ValueError(
'DoFn %r has multiple StateSpecs with the same name: %s.' %
(dofn, all_state_specs))
if len(all_timer_specs) != len(set(s.name for s in all_timer_specs)):
raise ValueError(
'DoFn %r has multiple TimerSpecs with the same name: %s.' %
(dofn, all_timer_specs))
# Reject DoFns that use timer specs without corresponding timer callbacks.
for timer_spec in all_timer_specs:
if not timer_spec._attached_callback:
raise ValueError((
'DoFn %r has a TimerSpec without an associated on_timer '
'callback: %s.') % (dofn, timer_spec))
method_name = timer_spec._attached_callback.__name__
if (timer_spec._attached_callback != getattr(dofn, method_name,
None).__func__): # type: ignore[union-attr]
raise ValueError((
'The on_timer callback for %s is not the specified .%s method '
'for DoFn %r (perhaps it was overwritten?).') %
(timer_spec, method_name, dofn))
class BaseTimer(object):
def clear(self, dynamic_timer_tag: str = '') -> None:
raise NotImplementedError
def set(self, timestamp: Timestamp, dynamic_timer_tag: str = '') -> None:
raise NotImplementedError
_TimerTuple = collections.namedtuple('timer_tuple', ('cleared', 'timestamp')) # type: ignore[name-match]
class RuntimeTimer(BaseTimer):
"""Timer interface object passed to user code."""View on GitHub (pinned to 12126d8942)