{"record":{"id":"565537ed98e6f52b","repo":"hashicorp/nomad","slug":"heap-doesn-t-contain-job-q-s","errorCode":null,"errorMessage":"heap doesn't contain job %q (%s)","messagePattern":"heap doesn't contain job %q \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/periodic.go","lineNumber":546,"sourceCode":"\t}\n\t_, ok := p.index[tuple]\n\treturn ok\n}\n\nfunc (p *periodicHeap) Update(job *structs.Job, next time.Time) error {\n\ttuple := structs.NamespacedID{\n\t\tID:        job.ID,\n\t\tNamespace: job.Namespace,\n\t}\n\tif pJob, ok := p.index[tuple]; ok {\n\t\t// Need to update the job as well because its spec can change.\n\t\tpJob.job = job\n\t\tpJob.next = next\n\t\theap.Fix(&p.heap, pJob.index)\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"heap doesn't contain job %q (%s)\", job.ID, job.Namespace)\n}\n\nfunc (p *periodicHeap) Remove(job *structs.Job) error {\n\ttuple := structs.NamespacedID{\n\t\tID:        job.ID,\n\t\tNamespace: job.Namespace,\n\t}\n\tif pJob, ok := p.index[tuple]; ok {\n\t\theap.Remove(&p.heap, pJob.index)\n\t\tdelete(p.index, tuple)\n\t\treturn nil\n\t}\n\n\treturn fmt.Errorf(\"heap doesn't contain job %q (%s)\", job.ID, job.Namespace)\n}\n\nfunc (p *periodicHeap) Length() int {\n\treturn len(p.heap)","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/periodic.go#L528-L564","documentation":"periodicHeap.Update could not find the job's namespace/ID tuple in the heap index, so there is nothing to re-heapify; the job was never added (or already removed) before the update was attempted.","triggerScenarios":"PeriodicDispatch.Add taking the update branch (job considered tracked) or dispatch (job not yet dispatched) calling Update for a tuple missing from p.index — the job was removed concurrently or never pushed.","commonSituations":"Updating a periodic job that was deregistered between check and update; dispatching a job that was just removed; race between Remove and Add under a lost lock; restore gaps.","solutions":["Push the job into the heap before updating it","Check for a concurrent removal that raced the update","Fix caller ordering so Add precedes Update"],"exampleFix":"// before\nerr := p.heap.Update(job, next) // fails if absent\n// after\nif err := p.heap.Update(job, next); err != nil {\n    if err2 := p.heap.Push(job, next); err2 != nil { return err2 }\n}","handlingStrategy":"validation","validationCode":"// Go: confirm presence before Update\nif _, ok := p.index[tuple]; !ok {\n    return p.Push(job, next)\n}\nreturn p.Update(job, next)","typeGuard":null,"tryCatchPattern":"// Go\nif err := p.Update(job, next); err != nil {\n    if strings.Contains(err.Error(), \"heap doesn't contain\") {\n        return p.Push(job, next)\n    }\n    return err\n}","preventionTips":["Perform check-and-mutate under the dispatcher lock","Avoid concurrent Remove/Update of the same job tuple","Rebuild heap from p.tracked when diverging"],"tags":["nomad","scheduler","heap","not-found"],"backgroundTag":"entity-not-found","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"}