{"record":{"id":"3f8de5ea9fcbbeac","repo":"hashicorp/nomad","slug":"failed-to-add-job-v-v","errorCode":null,"errorMessage":"failed to add job %v: %v","messagePattern":"failed to add job (.+?): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/periodic.go","lineNumber":228,"sourceCode":"\n\t\t// If the job is disabled and we aren't tracking it, do nothing.\n\t\treturn nil\n\t}\n\n\t// Add or update the job.\n\tp.tracked[tuple] = job\n\tnext, err := job.Periodic.Next(time.Now().In(job.Periodic.GetLocation()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed adding job %s: %v\", job.NamespacedID(), err)\n\t}\n\tif tracked {\n\t\tif err := p.heap.Update(job, next); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to update job %q (%s) launch time: %v\", job.ID, job.Namespace, err)\n\t\t}\n\t\tp.logger.Debug(\"updated periodic job\", \"job\", job.NamespacedID())\n\t} else {\n\t\tif err := p.heap.Push(job, next); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to add job %v: %v\", job.ID, err)\n\t\t}\n\t\tp.logger.Debug(\"registered periodic job\", \"job\", job.NamespacedID())\n\t}\n\n\t// Signal an update.\n\tselect {\n\tcase p.updateCh <- struct{}{}:\n\tdefault:\n\t}\n\n\treturn nil\n}\n\n// Remove stops tracking the passed job. If the job is not tracked, it is a\n// no-op.\nfunc (p *PeriodicDispatch) Remove(namespace, jobID string) error {\n\tp.l.Lock()\n\tdefer p.l.Unlock()","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/periodic.go#L210-L246","documentation":"This error is returned by PeriodicDispatch.Add when pushing a new periodic job onto the internal periodic heap fails. The heap Push only fails when a job with the same ID and namespace is already tracked, so this signals a duplicate registration attempt for a periodic job.","triggerScenarios":"Calling Add (directly or via applyUpsertJob RPC or restorePeriodicDispatcher) for a periodic job whose NamespacedID (ID+Namespace) is already present in the dispatcher's heap, i.e. the heap wasn't updated/removed beforehand.","commonSituations":"Re-registering the same periodic job without deregistering it first; restoring state on leader election where the job is already in the heap; racing upserts of the same job ID in the same namespace.","solutions":["Update the existing job instead of adding it (the Add path takes the update branch when the job is tracked — ensure you pass the current job object so the tracked lookup succeeds).","Remove the existing job via PeriodicDispatch.Remove before calling Add again.","Check for duplicate job IDs/namespaces in your job submissions.","If seen during restore, verify state store restore logic doesn't re-add already-tracked jobs."],"exampleFix":"// before\np.heap.Push(job, next) // panics/errors if already tracked\n// after\nif _, tracked := p.tracked[tuple]; tracked {\n    if err := p.heap.Update(job, next); err != nil { return err }\n} else if err := p.heap.Push(job, next); err != nil {\n    return fmt.Errorf(\"failed to add job %v: %v\", job.ID, err)\n}","handlingStrategy":"validation","validationCode":"// Go: check the job isn't already tracked before Add\nif _, exists := dispatcher.tracked[job.NamespacedID()]; exists {\n    // treat as update path or skip\n}","typeGuard":null,"tryCatchPattern":"// Go\nif err := d.Add(job); err != nil {\n    if strings.Contains(err.Error(), \"failed to add job\") {\n        return d.Remove(job) // then re-add\n    }\n    return err\n}","preventionTips":["Ensure job IDs are unique per namespace before submission","Route all periodic registrations through the update-aware Add path","Guard against duplicate upserts in applyUpsertJob"],"tags":["nomad","scheduler","periodic-jobs","duplicate-entry"],"backgroundTag":"duplicate-entity-registration","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}