apache/beam · error

ProcessElement uses a TimerProvider, but no Timer fields are

Error message

ProcessElement uses a TimerProvider, but no Timer fields are defined in the DoFn

What it means

A DoFn implementing TimerProvider (it uses timers in ProcessElement) must declare at least one Timer field so the framework knows which timer families to set/clear. AsDoFn validation (fn.go) fails when the keyed DoFn uses a TimerProvider but exports no Timer fields. (The USE AT entries in Python notebooks are unrelated false positives.)

Source

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

		}
	} else {
		return fmt.Errorf("OnTimer and ProcessElement functions for DoFn should have exactly same emitters, emitters used in OnTimer: %v, emitters used in ProcessElement: %v", otExists, peExists)
	}

	return nil
}

func validateTimer(fn *DoFn, numIn mainInputs) error {
	pt, fieldNames := fn.PipelineTimers()

	if _, ok := fn.methods[processElementName].TimerProvider(); ok {
		if numIn == MainSingle {
			err := errors.Errorf("ProcessElement uses a TimerProvider, but is not keyed")
			return errors.SetTopLevelMsgf(err, "ProcessElement uses a TimerProvider, but is not keyed. "+
				"All stateful DoFns must take a key/value pair as an input.")
		}
		if len(pt) == 0 {
			err := errors.New("ProcessElement uses a TimerProvider, but no Timer fields are defined in the DoFn")
			return errors.SetTopLevelMsgf(err, "ProcessElement uses a TimerProvider, but no timer fields are defined in the DoFn"+
				", Ensure that your DoFn exports the Timer fields used to set and clear timers.")
		}
		timerKeys := make(map[string]string)
		for i, t := range pt {
			for timerFamilyID := range t.Timers() {
				if timer, ok := timerKeys[timerFamilyID]; ok {
					err := errors.Errorf("Duplicate timer key %v", timerFamilyID)
					return errors.SetTopLevelMsgf(err, "Duplicate timer family ID %v used by struct fields %v and %v. Ensure that timer family IDs are unique per DoFn", timerFamilyID, timer, fieldNames[i])
				}
				timerKeys[timerFamilyID] = fieldNames[i]
			}
		}
		if err := validateOnTimerFn(fn); err != nil {
			return err
		}
	} else {
		if len(pt) > 0 {

View on GitHub (pinned to 12126d8942)

Solutions

  1. Add exported Timer fields to the DoFn struct, e.g. `myTimer timerprovider.Timer` created with timerprovider.NewTimer(dofn, "family").
  2. Ensure the timer family IDs used in ProcessElement (t.Timers()) match the declared fields.
  3. Confirm the DoFn input is keyed (KV) — a TimerProvider also requires a keyed input or a different error appears.
  4. Review the Beam timers docs and examples (sdks/go/pkg/beam/state/timerstore) for correct field wiring.

Example fix

// before
type fn struct{}
func (fn *f) ProcessElement(k string, v int, tp timerprovider.Provider) { tp.Set(...) }
// after
type fn struct {
    MyTimer timerprovider.Timer
}
func (fn *f) ProcessElement(k string, v int, tp timerprovider.Provider) { fn.MyTimer.Set(...) }
Defensive patterns

Strategy: validation

Validate before calling

// Before using a TimerProvider DoFn, check for exported timer fields
for _, f := range reflect.TypeOf(fn).Fields() {
    if _, ok := f.Type.MethodByName("Timers"); ok { return nil }
}
return errors.New("DoFn uses TimerProvider but declares no Timer fields")

Type guard

func hasTimerFields(fn interface{}) bool {
    t := reflect.TypeOf(fn)
    for i := 0; i < t.NumField(); i++ {
        if t.Field(i).Type.Kind() == reflect.Struct && strings.Contains(t.Field(i).Type.String(), "timerprovider.Timer") {
            return true
        }
    }
    return false
}

Prevention

When it happens

Trigger: Declaring a stateful, keyed DoFn whose ProcessElement takes a TimerProvider (or calls timers) without defining embedded Timer fields like `Timer timerprovider.Timer` via timerprovider.NewTimer(...), or forgetting to embed/export the timer field on the struct.

Common situations: Building stateful Beam pipelines with timers for windowed retries or session management; refactoring that removed or unexported timer fields while ProcessElement still requested a TimerProvider.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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