{"record":{"id":"0a2526e16eb5bcec","repo":"hashicorp/nomad","slug":"must-specify-at-least-one-healthy-unhealthy-alloca","errorCode":null,"errorMessage":"must specify at least one healthy/unhealthy allocation ID","messagePattern":"must specify at least one healthy/unhealthy allocation ID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/deployment_endpoint.go","lineNumber":422,"sourceCode":"// deployment.\nfunc (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest, reply *structs.DeploymentUpdateResponse) error {\n\tauthErr := d.srv.Authenticate(d.ctx, args)\n\tif done, err := d.srv.forward(\"Deployment.SetAllocHealth\", args, args, reply); done {\n\t\treturn err\n\t}\n\td.srv.MeasureRPCRate(\"deployment\", structs.RateMetricWrite, args)\n\tif authErr != nil {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"deployment\", \"set_alloc_health\"}, time.Now())\n\n\t// Validate the arguments\n\tif args.DeploymentID == \"\" {\n\t\treturn fmt.Errorf(\"missing deployment ID\")\n\t}\n\n\tif len(args.HealthyAllocationIDs)+len(args.UnhealthyAllocationIDs) == 0 {\n\t\treturn fmt.Errorf(\"must specify at least one healthy/unhealthy allocation ID\")\n\t}\n\n\t// Lookup the deployment\n\tsnap, err := d.srv.fsm.State().Snapshot()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tws := memdb.NewWatchSet()\n\tdeploy, err := snap.DeploymentByID(ws, args.DeploymentID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif deploy == nil {\n\t\treturn fmt.Errorf(\"deployment not found\")\n\t}\n\n\t// Check namespace submit-job permissions","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/deployment_endpoint.go#L404-L440","documentation":"Deployment.SetAllocHealth requires at least one allocation ID in HealthyAllocationIDs or UnhealthyAllocationIDs. Recording health for zero allocations is meaningless, so the server rejects it after the ID check.","triggerScenarios":"Calling SetAllocHealth with a valid DeploymentID but both alloc ID slices empty — e.g. filtering allocations and matching none, or wiring only healthy IDs when there are none and no unhealthy IDs either.","commonSituations":"Canary health automation that selects allocs by status/name and gets an empty list (deploy just started, no allocs placed yet); script parsing `nomad job allocs` output incorrectly.","solutions":["Fetch the deployment's allocations (`nomad deployment status <id>`) and pass at least one alloc ID per the API contract","Gate the call: skip SetAllocHealth when there are no allocs to mark yet","If using `nomad deployment promote`/`unblock` instead, note those don't need alloc IDs"],"exampleFix":"// before\nif len(healthy) == 0 { /* still called anyway */ }\ncallSetAllocHealth(deployID, healthy, nil)\n// after\nif len(healthy) == 0 && len(unhealthy) == 0 {\n    return nil // nothing to mark; wait for allocs\n}\ncallSetAllocHealth(deployID, healthy, unhealthy)","handlingStrategy":"validation","validationCode":"allocs, _, _ := client.Deployments().Allocations(deployID, nil)\nif len(allocs) == 0 { return nil } // nothing to mark yet\nreq := &api.DeploymentAllocHealthRequest{\n    DeploymentID: deployID,\n    HealthyAllocationIDs: healthyIDs(allocs),\n    UnhealthyAllocationIDs: unhealthyIDs(allocs),\n}","typeGuard":"function hasAllocIDs(r) { return (r.HealthyAllocationIDs?.length ?? 0) > 0 || (r.UnhealthyAllocationIDs?.length ?? 0) > 0; }","tryCatchPattern":"try {\n  await setAllocHealth(req);\n} catch (e) {\n  if (String(e).includes(\"must specify at least one\")) {\n    // allocs not placed yet — poll deployment status and retry later\n    return pollAndRetry(deployID);\n  }\n  throw e;\n}","preventionTips":["List the deployment's allocations before marking health; skip if none exist","Ensure your alloc-selection filter can't produce an empty set silently","Wait for canary allocs to be placed before evaluating health"],"tags":["nomad","deployment","api","validation"],"backgroundTag":"missing-required-argument","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"}