{"record":{"id":"37a7809b6d757e2a","repo":"hashicorp/nomad","slug":"alloc-q-is-not-part-of-deployment-q","errorCode":null,"errorMessage":"alloc %q is not part of deployment %q","messagePattern":"alloc %q is not part of deployment %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/state/state_store.go","lineNumber":5167,"sourceCode":"\t\treturn fmt.Errorf(\"Deployment ID %q couldn't be updated as it does not exist\", req.DeploymentID)\n\t} else if !deployment.Active() {\n\t\treturn fmt.Errorf(\"Deployment %q has terminal status %q:\", deployment.ID, deployment.Status)\n\t}\n\n\t// Update the health status of each allocation\n\tif total := len(req.HealthyAllocationIDs) + len(req.UnhealthyAllocationIDs); total != 0 {\n\t\tsetAllocHealth := func(id string, healthy bool, ts time.Time) error {\n\t\t\texisting, err := txn.First(\"allocs\", \"id\", id)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"alloc %q lookup failed: %v\", id, err)\n\t\t\t}\n\t\t\tif existing == nil {\n\t\t\t\treturn fmt.Errorf(\"unknown alloc %q\", id)\n\t\t\t}\n\n\t\t\told := existing.(*structs.Allocation)\n\t\t\tif old.DeploymentID != req.DeploymentID {\n\t\t\t\treturn fmt.Errorf(\"alloc %q is not part of deployment %q\", id, req.DeploymentID)\n\t\t\t}\n\n\t\t\t// Set the health\n\t\t\tcopy := old.Copy()\n\t\t\tif copy.DeploymentStatus == nil {\n\t\t\t\tcopy.DeploymentStatus = &structs.AllocDeploymentStatus{}\n\t\t\t}\n\t\t\tcopy.DeploymentStatus.Healthy = new(healthy)\n\t\t\tcopy.DeploymentStatus.Timestamp = ts\n\t\t\tcopy.DeploymentStatus.ModifyIndex = index\n\t\t\tcopy.ModifyTime = req.Timestamp.UnixNano()\n\t\t\tcopy.ModifyIndex = index\n\n\t\t\tif err := s.updateDeploymentWithAlloc(index, copy, old, txn); err != nil {\n\t\t\t\treturn fmt.Errorf(\"error updating deployment: %v\", err)\n\t\t\t}\n\n\t\t\tif err := txn.Insert(\"allocs\", copy); err != nil {","sourceCodeStart":5149,"sourceCodeEnd":5185,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/state/state_store.go#L5149-L5185","documentation":"Thrown in setAllocHealth when the allocation exists but its Allocation.DeploymentID does not match the DeploymentID of the request. The deployment-health endpoint only allows updating allocs that belong to the referenced deployment, preventing cross-deployment health contamination. The entire Raft transaction aborts with no changes applied.","triggerScenarios":"A DeploymentAllocHealthRequest lists an alloc ID that belongs to a different (typically older) deployment of the same job — e.g. after a resubmit, canary failover, or job update that created a new deployment while the caller still sends alloc IDs gathered from the old one.","commonSituations":"Job stopped and resubmitted: new deployment ID but tooling caches old alloc list; monitoring scripts polling allocs of the job rather than of the specific deployment; canary promotion flows mixing allocs across deployments.","solutions":["Re-resolve allocs via the deployment itself (allocs where DeploymentID == req.DeploymentID) instead of job-wide alloc lists","Filter the alloc list before submitting: drop any alloc whose DeploymentID differs from the target deployment","After a job resubmit, fetch the fresh deployment ID (latest deployment for the job) and rebuild the health request","Check for races between evaluation completion and health submission; re-read state at submission time"],"exampleFix":"// before\nfor _, a := range allocsForJob(jobID) { ids = append(ids, a.ID) }\n// after\nfor _, a := range allocsForJob(jobID) {\n\tif a.DeploymentID == depID { // only allocs of THIS deployment\n\t\tids = append(ids, a.ID)\n\t}\n}","handlingStrategy":"validation","validationCode":"func allocsOfDeployment(allocs []*structs.Allocation, depID string) []string {\n\tvar out []string\n\tfor _, a := range allocs {\n\t\tif a.DeploymentID == depID { out = append(out, a.ID) }\n\t}\n\treturn out\n}","typeGuard":"func belongsToDeployment(a *structs.Allocation, depID string) bool { return a != nil && a.DeploymentID == depID }","tryCatchPattern":"if err := store.UpsertDeploymentAllocHealth(idx, req); err != nil {\n\tif strings.Contains(err.Error(), \"is not part of deployment\") {\n\t\t// rebuild request from allocs where DeploymentID == req.DeploymentID\n\t}\n\treturn err\n}","preventionTips":["Always source alloc IDs from the target deployment, never from job-wide lists","After any job resubmit/canary failover, re-fetch the deployment ID and its allocs","Assert a.DeploymentID == depID before adding an alloc to a health request","Avoid parallel health submitters that may hold different deployment generations"],"tags":["nomad","state-store","deployment","allocation","mismatch"],"backgroundTag":"entity-ownership-mismatch","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"}