apache/beam · error · ValueError

@on_timer decorator expected callable.

Error message

@on_timer decorator expected callable.

What it means

The function returned by @on_timer validates that the method being decorated is callable before attaching it to the TimerSpec. If the object under the decorator is not callable (e.g. a property, a constant, or a value), the decorator raises ValueError instead of silently installing a broken callback.

Solutions

  1. Decorate a real method: keep @on_timer(TIMER_SPEC) directly above a def with self as first parameter
  2. Check decorator ordering — @on_timer should wrap the actual function (place non-callable-returning decorators appropriately)
  3. Remove stray decorators that turn the method into a non-callable

Example fix

// before
@on_timer(TIMER_SPEC)
@staticmethod
def expiry(self): ...
// after
@on_timer(TIMER_SPEC)
def expiry(self): ...
Defensive patterns

Strategy: validation

Validate before calling

assert callable(method_to_decorate), '@on_timer must decorate a callable method'

Type guard

def is_callable_method(m) -> bool:
    return callable(m)

Try / catch

try:
    _ = MyDoFn()
except ValueError as e:
    if 'expected callable' in str(e):
        log.error('check decorator ordering on timer callback')

Prevention

When it happens

Trigger: Applying @on_timer(TIMER_SPEC) on top of a non-callable expression, e.g. stacking decorators that replace the method with a value, or accidentally decorating a class attribute instead of a method.

Common situations: Decorator-order mistakes (e.g. combining @staticmethod/@property with @on_timer in the wrong order); partially edited code where the def line was removed but the decorator remained.

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/42665b720aa7ae74. Report an issue: GitHub.

Appendix: source

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

  """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
      timer and state specs.
  """

  # Avoid circular import.

View on GitHub (pinned to 12126d8942)