{"record":{"id":"4261e8b04bf43bf4","repo":"apache/beam","slug":"processelement-uses-a-timerprovider-but-no-timer-fields-are","errorCode":null,"errorMessage":"ProcessElement uses a TimerProvider, but no Timer fields are defined in the DoFn","messagePattern":"ProcessElement uses a TimerProvider, but no Timer fields are defined in the DoFn","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/fn.go","lineNumber":1429,"sourceCode":"\t\t}\n\t} else {\n\t\treturn 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)\n\t}\n\n\treturn nil\n}\n\nfunc validateTimer(fn *DoFn, numIn mainInputs) error {\n\tpt, fieldNames := fn.PipelineTimers()\n\n\tif _, ok := fn.methods[processElementName].TimerProvider(); ok {\n\t\tif numIn == MainSingle {\n\t\t\terr := errors.Errorf(\"ProcessElement uses a TimerProvider, but is not keyed\")\n\t\t\treturn errors.SetTopLevelMsgf(err, \"ProcessElement uses a TimerProvider, but is not keyed. \"+\n\t\t\t\t\"All stateful DoFns must take a key/value pair as an input.\")\n\t\t}\n\t\tif len(pt) == 0 {\n\t\t\terr := errors.New(\"ProcessElement uses a TimerProvider, but no Timer fields are defined in the DoFn\")\n\t\t\treturn errors.SetTopLevelMsgf(err, \"ProcessElement uses a TimerProvider, but no timer fields are defined in the DoFn\"+\n\t\t\t\t\", Ensure that your DoFn exports the Timer fields used to set and clear timers.\")\n\t\t}\n\t\ttimerKeys := make(map[string]string)\n\t\tfor i, t := range pt {\n\t\t\tfor timerFamilyID := range t.Timers() {\n\t\t\t\tif timer, ok := timerKeys[timerFamilyID]; ok {\n\t\t\t\t\terr := errors.Errorf(\"Duplicate timer key %v\", timerFamilyID)\n\t\t\t\t\treturn 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])\n\t\t\t\t}\n\t\t\t\ttimerKeys[timerFamilyID] = fieldNames[i]\n\t\t\t}\n\t\t}\n\t\tif err := validateOnTimerFn(fn); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif len(pt) > 0 {","sourceCodeStart":1411,"sourceCodeEnd":1447,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/fn.go#L1411-L1447","documentation":"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.)","triggerScenarios":"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.","commonSituations":"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.","solutions":["Add exported Timer fields to the DoFn struct, e.g. `myTimer timerprovider.Timer` created with timerprovider.NewTimer(dofn, \"family\").","Ensure the timer family IDs used in ProcessElement (t.Timers()) match the declared fields.","Confirm the DoFn input is keyed (KV) — a TimerProvider also requires a keyed input or a different error appears.","Review the Beam timers docs and examples (sdks/go/pkg/beam/state/timerstore) for correct field wiring."],"exampleFix":"// before\ntype fn struct{}\nfunc (fn *f) ProcessElement(k string, v int, tp timerprovider.Provider) { tp.Set(...) }\n// after\ntype fn struct {\n    MyTimer timerprovider.Timer\n}\nfunc (fn *f) ProcessElement(k string, v int, tp timerprovider.Provider) { fn.MyTimer.Set(...) }","handlingStrategy":"validation","validationCode":"// Before using a TimerProvider DoFn, check for exported timer fields\nfor _, f := range reflect.TypeOf(fn).Fields() {\n    if _, ok := f.Type.MethodByName(\"Timers\"); ok { return nil }\n}\nreturn errors.New(\"DoFn uses TimerProvider but declares no Timer fields\")","typeGuard":"func hasTimerFields(fn interface{}) bool {\n    t := reflect.TypeOf(fn)\n    for i := 0; i < t.NumField(); i++ {\n        if t.Field(i).Type.Kind() == reflect.Struct && strings.Contains(t.Field(i).Type.String(), \"timerprovider.Timer\") {\n            return true\n        }\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Create Timer fields with timerprovider.NewTimer(dofn, \"family\") and keep them exported.","Match timer family IDs used in ProcessElement with declared fields.","Ensure stateful DoFns take KV inputs when using TimerProvider."],"tags":["go","beam","timers","dofn","stateful"],"backgroundTag":"missing-required-config-field","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}