apache/beam · error

OnTimer function not defined for DoFn: %v. Ensure that OnTim

Error message

OnTimer function not defined for DoFn: %v. Ensure that OnTimer function is implemented for the DoFn.

What it means

Beam Go requires any DoFn participating in timers to implement an OnTimer method. When validation detects a DoFn set up for timers (or pipeline validation reaches the timer check) but fn.methods has no entry for onTimerName, this error is thrown: callbacks fired by timers have nowhere to be dispatched.

Source

Thrown at sdks/go/pkg/beam/core/graph/fn.go:1393

			}
			stateKeys[k] = s
		}
	} else {
		if len(ps) > 0 {
			err := errors.Errorf("ProcessElement doesn't use a StateProvider, but State structs are attached to "+
				"the DoFn: %v", ps)
			return errors.SetTopLevelMsgf(err, "ProcessElement doesn't use a StateProvider, but State structs are "+
				"attached to the DoFn: %v\nEnsure that you are using the StateProvider to perform any reads or writes"+
				"of pipeline state.", ps)
		}
	}

	return nil
}

func validateOnTimerFn(fn *DoFn) error {
	if _, ok := fn.methods[onTimerName]; !ok {
		err := errors.Errorf("OnTimer function not defined for DoFn: %v", fn.Name())
		return errors.SetTopLevelMsgf(err, "OnTimer function not defined for DoFn: %v. Ensure that OnTimer function is implemented for the DoFn.", fn.Name())
	}

	pipelineTimers, _ := fn.PipelineTimers()

	if _, ok := fn.methods[onTimerName].TimerProvider(); !ok {
		err := errors.Errorf("OnTimer function doesn't use a TimerProvider, but Timer field is attached to the DoFn(%v): %v", fn.Name(), pipelineTimers)
		return errors.SetTopLevelMsgf(err, "OnTimer function doesn't use a TimerProvider, but Timer field is attached to the DoFn(%v): %v"+
			", Ensure that you are using the TimerProvider to set and clear the timers.", fn.Name(), pipelineTimers)
	}

	_, otNum, otExists := fn.methods[onTimerName].Emits()
	_, peNum, peExists := fn.methods[processElementName].Emits()

	if otExists == peExists {
		if otNum != peNum {
			return fmt.Errorf("OnTimer and ProcessElement functions for DoFn should have exactly same emitters, no. of emitters used in OnTimer: %v, no. of emitters used in ProcessElement: %v", otNum, peNum)
		}

View on GitHub (pinned to 12126d8942)

Solutions

  1. Implement the OnTimer method on the DoFn with the correct signature (context, timer.Set/OnTimer callback args).
  2. Match the timer IDs set via the TimerProvider with the ones handled in OnTimer.
  3. Refer to Beam's timer example DoFn and copy the complete pair (ProcessElement + OnTimer).

Example fix

// before
type BatchFn struct{ T timer.Provider }
func (fn *BatchFn) ProcessElement(ctx context.Context, tp timer.Provider, k beam.KV[string, string]) error {
    tp.Set(...) // timers set, no OnTimer defined
}
// after
func (fn *BatchFn) OnTimer(ctx context.Context, ts timer.Signals, k beam.KV[string, string]) error {
    // handle expiry
}
Defensive patterns

Strategy: validation

Validate before calling

var _ interface {
    OnTimer(context.Context, timer.Signals, beam.KV[string, string]) error
} = (*BatchFn)(nil) // compile-time check that OnTimer is implemented

Type guard

func implementsOnTimer(fn interface{}) bool {
    _, ok := fn.(interface{ OnTimer(context.Context, timer.Signals) error })
    return ok
}

Try / catch

if err := beam.Run(ctx, p); err != nil {
    if strings.Contains(err.Error(), "OnTimer function not defined") {
        log.Fatalf("implement OnTimer for %T: %v", dofn, err)
    }
    return err
}

Prevention

When it happens

Trigger: Attaching Timer fields or a TimerProvider to a DoFn but not defining func (fn *X) OnTimer(ctx, ts, t) ...; implementing ProcessElement with timer logic and forgetting the OnTimer callback.

Common situations: Setting event-time timers in ProcessElement without defining the handler; copying only the ProcessElement half of a timer example; renames/refactors dropping the OnTimer method.

Related errors


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