apache/beam · error · ValueError

@on_timer decorator expected TimerSpec.

Error message

@on_timer decorator expected TimerSpec.

What it means

The @on_timer decorator requires its first argument to be a TimerSpec instance, because it attaches the decorated method to that spec's _attached_callback slot. Passing anything else (a string name, a StateSpec, None) is rejected with a ValueError at decoration time.

Solutions

  1. Pass the TimerSpec instance: @on_timer(MY_TIMER_SPEC) where MY_TIMER_SPEC = TimerSpec('my_timer', TimeDomain.WATERMARK)
  2. Define the TimerSpec at class level first and reference the object, not its name string
  3. Ensure the spec argument is not a StateSpec or other spec type

Example fix

// before
@on_timer('my_timer')
def expiry(self): ...
// after
TIMER_SPEC = TimerSpec('my_timer', TimeDomain.WATERMARK)
@on_timer(TIMER_SPEC)
def expiry(self): ...
Defensive patterns

Strategy: validation

Validate before calling

assert isinstance(TIMER_SPEC, TimerSpec), 'on_timer requires a TimerSpec instance'

Type guard

from apache_beam.transforms.userstate import TimerSpec
def is_timer_spec(x) -> bool:
    return isinstance(x, TimerSpec)

Try / catch

try:
    _ = MyDoFn()  # decoration errors surface at class-definition/import time
except ValueError as e:
    if 'on_timer' in str(e):
        fix_timer_decorator()

Prevention

When it happens

Trigger: Writing @on_timer('my_timer') with the timer's name string instead of the TimerSpec object; passing a StateSpec or custom object; forgetting to define the TimerSpec constant being referenced.

Common situations: Confusing @on_timer with stateful DoFn parameter injection where timers are passed by TimerSpec via @stateful; copy-pasting examples and replacing the spec constant with a name; older Beam examples using string-based timer references.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/c620e70cd9123b3e. Report an issue: GitHub.

Appendix: source

Thrown at sdks/python/apache_beam/transforms/userstate.py:233

            coders._TimerCoder(key_coder, window_coder)))


def on_timer(timer_spec: TimerSpec) -> Callable[[CallableT], CallableT]:
  """Decorator for timer firing DoFn method.

  This decorator allows a user to specify an on_timer processing method
  in a stateful DoFn.  Sample usage::

    class MyDoFn(DoFn):
      TIMER_SPEC = TimerSpec('timer', TimeDomain.WATERMARK)

      @on_timer(TIMER_SPEC)
      def my_timer_expiry_callback(self):
        logging.info('Timer expired!')
  """

  if not isinstance(timer_spec, TimerSpec):
    raise ValueError('@on_timer decorator expected TimerSpec.')

  def _inner(method: CallableT) -> CallableT:
    if not callable(method):
      raise ValueError('@on_timer decorator expected callable.')
    if timer_spec._attached_callback:
      raise ValueError(
          'Multiple on_timer callbacks registered for %r.' % timer_spec)
    timer_spec._attached_callback = method
    return method

  return _inner


def get_dofn_specs(dofn: 'DoFn') -> tuple[set[StateSpec], set[TimerSpec]]:
  """Gets the state and timer specs for a DoFn, if any.

  Args:
    dofn (apache_beam.transforms.core.DoFn): The DoFn instance to introspect for

View on GitHub (pinned to 12126d8942)