{"record":{"id":"a8c647edd1fb6bc5","repo":"kubernetes/kubernetes","slug":"adding-pod-event-handler-w-a8c647","errorCode":null,"errorMessage":"adding Pod event handler: %w","messagePattern":"adding Pod event handler: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/controller/job/job_controller.go","lineNumber":299,"sourceCode":"\t\t},\n\t}); err != nil {\n\t\treturn nil, fmt.Errorf(\"adding Job event handler: %w\", err)\n\t}\n\tjm.jobLister = jobInformer.Lister()\n\tjm.jobStoreSynced = jobInformer.Informer().HasSynced\n\n\tif _, err := podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{\n\t\tAddFunc: func(obj interface{}) {\n\t\t\tjm.addPod(logger, obj)\n\t\t},\n\t\tUpdateFunc: func(oldObj, newObj interface{}) {\n\t\t\tjm.updatePod(logger, oldObj, newObj)\n\t\t},\n\t\tDeleteFunc: func(obj interface{}) {\n\t\t\tjm.deletePod(logger, obj, true)\n\t\t},\n\t}); err != nil {\n\t\treturn nil, fmt.Errorf(\"adding Pod event handler: %w\", err)\n\t}\n\tjm.podStore = podInformer.Lister()\n\tjm.podStoreSynced = podInformer.Informer().HasSynced\n\n\terr := controller.AddPodControllerIndexer(podInformer.Informer())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"adding Pod controller UID indexer: %w\", err)\n\t}\n\tjm.podIndexer = podInformer.Informer().GetIndexer()\n\n\tjm.updateStatusHandler = jm.updateJobStatus\n\tjm.syncHandler = jm.syncJob\n\n\tif feature.DefaultFeatureGate.Enabled(features.WorkloadWithJob) {\n\t\tif err := jm.addSchedulingInformers(logger, workloadInformer, podGroupInformer); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/kubernetes/kubernetes/blob/b882c60b4023bdf09264c2d5d30a2cadebc240fb/pkg/controller/job/job_controller.go#L281-L317","documentation":"Returned by NewController when `podInformer.Informer().AddEventHandler(...)` fails (job_controller.go:288-300). Same lifecycle semantics as the Job handler error: handlers must be registered before the informer runs. Failure aborts controller creation.","triggerScenarios":"Pod informer already started; podInformer factory misconfigured; pod listwatch GVR (`\"\"/v1/pods`) unavailable; reusing a stopped shared informer.","commonSituations":"Embedding the job controller and starting the core shared informer factory too early; apiserver temporarily refusing pod list/watch; client-go version skew.","solutions":["Register all handlers (Job + Pod + indexer) before `informerFactory.Start`.","Ensure the core/v1 pod informer comes from the same factory passed to NewController.","Inspect the wrapped error for a permissions/listwatch cause.","Confirm kubeClient has list/watch on pods."],"exampleFix":"// before: core factory started, then pod handler added\ncoreInformerFactory.Start(ctx.Done())\njm, err := job.NewController(... coreInformerFactory.Core().V1().Pods() ...)\n// after\njm, err := job.NewController(... coreInformerFactory.Core().V1().Pods() ...)\ncoreInformerFactory.Start(ctx.Done())","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Same lifecycle guard as the Job handler: register Pod handler before factory.Start.\nfunc buildThenStart(ctx context.Context, coreFactory informers.SharedInformerFactory, build func() error) error {\n    if err := build(); err != nil { return err }   // adds Pod handler + indexer\n    coreFactory.Start(ctx.Done())\n    return nil\n}","preventionTips":["Register Pod handler before the core informer factory starts.","Source the pod informer from the same factory passed to NewController.","Ensure kubeClient has list/watch on pods.","Keep client-go and job controller versions aligned."],"tags":["kubernetes","job-controller","informer","lifecycle","startup"],"analyzedSha":"b882c60b4023bdf09264c2d5d30a2cadebc240fb","analyzedAt":"2026-08-07T04:07:48.144Z","schemaVersion":2},"datasetVersion":"2026-08-07T07:17:06.508Z"}