{"record":{"id":"fefdc017014e0693","repo":"hashicorp/nomad","slug":"deletejob-failed-w","errorCode":null,"errorMessage":"DeleteJob failed: %w","messagePattern":"DeleteJob failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/fsm.go","lineNumber":899,"sourceCode":"\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttransition := &structs.DesiredTransition{NoShutdownDelay: new(true)}\n\t\tfor _, alloc := range allocs {\n\t\t\terr := n.state.UpdateAllocDesiredTransitionTxn(tx, index, alloc.ID, transition)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\terr = tx.Insert(\"index\", &state.IndexEntry{Key: \"allocs\", Value: index})\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"index update failed: %v\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\tif purge {\n\t\tif err := n.state.DeleteJobTxn(index, namespace, jobID, tx); err != nil {\n\t\t\treturn fmt.Errorf(\"DeleteJob failed: %w\", err)\n\t\t}\n\n\t\t// We always delete from the periodic launch table because it is possible that\n\t\t// the job was updated to be non-periodic, thus checking if it is periodic\n\t\t// doesn't ensure we clean it up properly.\n\t\tn.state.DeletePeriodicLaunchTxn(index, namespace, jobID, tx)\n\t\treturn nil\n\t}\n\n\t// Get the current job and mark it as stopped and re-insert it.\n\tws := memdb.NewWatchSet()\n\tcurrent, err := n.state.JobByIDTxn(ws, namespace, jobID, tx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"JobByID lookup failed: %w\", err)\n\t}\n\n\tif current == nil {\n\t\treturn fmt.Errorf(\"job %q in namespace %q doesn't exist to be deregistered\", jobID, namespace)","sourceCodeStart":881,"sourceCodeEnd":917,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/fsm.go#L881-L917","documentation":"When a deregistration request has purge=true, handleJobDeregister deletes the job permanently via state.DeleteJobTxn. If the state-store delete fails (wrapped error), the FSM apply aborts. This means the job could not be removed from the persistent state within the transaction.","triggerScenarios":"Job deregister with purge=true where DeleteJobTxn fails — e.g. job record missing in txn expectations, internal memdb error, or index constraint violation.","commonSituations":"Purging an already-purged job concurrently, corrupted state, or version-mismatched snapshot restore where expected records are absent.","solutions":["Check the wrapped error; if it is 'job not found', the job is already gone — no action needed.","Verify job existence with `nomad job status <jobID>` before purging.","Retry the purge after any in-flight deregistration completes.","Restore from a consistent snapshot if the state store appears corrupted."],"exampleFix":"// avoid double-purge: check the job exists first\njob, _, err := client.Jobs().Info(jobID, nil)\nif err == nil {\n    _, _, err = client.Jobs().Deregister(jobID, true, nil) // purge\n}","handlingStrategy":"validation","validationCode":"// check the job still exists before purging\nif _, _, err := client.Jobs().Info(jobID, &nomad.QueryOptions{Namespace: ns}); err != nil {\n    // job already gone; skip purge\n}","typeGuard":null,"tryCatchPattern":"_, _, err := client.Jobs().Deregister(jobID, true, nil)\nif err != nil && strings.Contains(err.Error(), \"DeleteJob failed\") {\n    // check if 'not found' -> already purged; otherwise escalate\n}","preventionTips":["Make purge operations idempotent in automation","Verify job existence before purge","Avoid concurrent duplicate deregistration requests"],"tags":["nomad","raft","fsm","job-purge"],"backgroundTag":"job-delete-failed","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"}