{"record":{"id":"c4e6cd3f91dfef27","repo":"apache/beam","slug":"ontimer-function-doesn-t-use-a-timerprovider-but-timer-field","errorCode":null,"errorMessage":"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.","messagePattern":"OnTimer function doesn't use a TimerProvider, but Timer field is attached to the DoFn\\((.+?)\\): (.+?), Ensure that you are using the TimerProvider to set and clear the timers\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/core/graph/fn.go","lineNumber":1400,"sourceCode":"\t\t\treturn errors.SetTopLevelMsgf(err, \"ProcessElement doesn't use a StateProvider, but State structs are \"+\n\t\t\t\t\"attached to the DoFn: %v\\nEnsure that you are using the StateProvider to perform any reads or writes\"+\n\t\t\t\t\"of pipeline state.\", ps)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc validateOnTimerFn(fn *DoFn) error {\n\tif _, ok := fn.methods[onTimerName]; !ok {\n\t\terr := errors.Errorf(\"OnTimer function not defined for DoFn: %v\", fn.Name())\n\t\treturn errors.SetTopLevelMsgf(err, \"OnTimer function not defined for DoFn: %v. Ensure that OnTimer function is implemented for the DoFn.\", fn.Name())\n\t}\n\n\tpipelineTimers, _ := fn.PipelineTimers()\n\n\tif _, ok := fn.methods[onTimerName].TimerProvider(); !ok {\n\t\terr := errors.Errorf(\"OnTimer function doesn't use a TimerProvider, but Timer field is attached to the DoFn(%v): %v\", fn.Name(), pipelineTimers)\n\t\treturn errors.SetTopLevelMsgf(err, \"OnTimer function doesn't use a TimerProvider, but Timer field is attached to the DoFn(%v): %v\"+\n\t\t\t\", Ensure that you are using the TimerProvider to set and clear the timers.\", fn.Name(), pipelineTimers)\n\t}\n\n\t_, otNum, otExists := fn.methods[onTimerName].Emits()\n\t_, peNum, peExists := fn.methods[processElementName].Emits()\n\n\tif otExists == peExists {\n\t\tif otNum != peNum {\n\t\t\treturn 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)\n\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","sourceCodeStart":1382,"sourceCodeEnd":1418,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/core/graph/fn.go#L1382-L1418","documentation":"This error fires when a DoFn defines Timer fields but its OnTimer method does not take a TimerProvider parameter. Beam's timers model requires timer access (setting/clearing) to flow through the TimerProvider; attached Timer fields that OnTimer never receives indicate dead or miswired configuration.","triggerScenarios":"Declaring exported timer fields on the DoFn while OnTimer's signature lacks timer.Provider (or ProcessElement/OnTimer were refactored so the provider parameter was dropped).","commonSituations":"Removing the TimerProvider argument during a signature refactor while leaving the fields; copying only the fields from a timer example without the provider parameters; adding timers to an existing DoFn without updating OnTimer.","solutions":["Add the timer.Provider parameter to OnTimer (and ProcessElement where needed) and use it to set/clear timers.","Remove the unused Timer fields if the DoFn no longer uses timers.","Compare with a working Beam timer DoFn example and align all signatures."],"exampleFix":"// before\nfunc (fn *BatchFn) OnTimer(ctx context.Context, ts timer.Signals, k beam.KV[string, string]) error { ... }\n// after\nfunc (fn *BatchFn) OnTimer(ctx context.Context, tp timer.Provider, ts timer.Signals, k beam.KV[string, string]) error {\n    tp.Set(...)\n}","handlingStrategy":"validation","validationCode":"// OnTimer must accept timer.Provider if Timer fields are declared\nt := reflect.TypeOf(fn)\nfor i := 0; i < t.NumField(); i++ {\n    if strings.Contains(fmt.Sprint(t.Field(i).Type), \"timer.\") {\n        // verify method signature includes timer.Provider\n        break\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := beam.Run(ctx, p); err != nil {\n    if strings.Contains(err.Error(), \"doesn't use a TimerProvider, but Timer field is attached\") {\n        log.Fatalf(\"add timer.Provider to OnTimer or remove unused Timer fields: %v\", err)\n    }\n    return err\n}","preventionTips":["Keep Timer fields and timer.Provider parameters in sync across OnTimer and ProcessElement","Delete Timer fields when removing timer logic","Match signatures against Beam's official timer examples"],"tags":["go","apache-beam","dofn","timers","signature-validation"],"backgroundTag":"missing-required-argument","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"}