{"record":{"id":"bd09b76ee4d61bb2","repo":"hashicorp/nomad","slug":"invalid-node-pool-capability-s-for-s","errorCode":null,"errorMessage":"Invalid node pool capability '%s' for '%s'","messagePattern":"Invalid node pool capability '(.+?)' for '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"acl/policy.go","lineNumber":629,"sourceCode":"\t\t\t\tpathPolicy.Capabilities = expandVariablesCapabilities(pathPolicy.Capabilities)\n\n\t\t\t}\n\t\t}\n\n\t\t// Remove the namespace name from the extra key list.\n\t\tp.removeExtraKey(ns.Name)\n\t}\n\n\tfor _, np := range p.NodePools {\n\t\tif !validNodePool.MatchString(np.Name) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid node pool name '%s'\", np.Name)\n\t\t}\n\t\tif np.Policy != \"\" && !isPolicyValid(np.Policy) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid node pool policy '%s' for '%s'\", np.Policy, np.Name)\n\t\t}\n\t\tfor _, cap := range np.Capabilities {\n\t\t\tif !isNodePoolCapabilityValid(cap) {\n\t\t\t\treturn nil, fmt.Errorf(\"Invalid node pool capability '%s' for '%s'\", cap, np.Name)\n\t\t\t}\n\t\t}\n\n\t\tif np.Policy != \"\" {\n\t\t\textraCap := expandNodePoolPolicy(np.Policy)\n\t\t\tnp.Capabilities = append(np.Capabilities, extraCap...)\n\t\t}\n\n\t\t// Remove the node-pool name from the extra key list.\n\t\tp.removeExtraKey(np.Name)\n\t}\n\n\tfor _, hv := range p.HostVolumes {\n\t\tif !validVolume.MatchString(hv.Name) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid host volume name: %#v\", hv)\n\t\t}\n\t\tif hv.Policy != \"\" && !isPolicyValid(hv.Policy) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid host volume policy: %#v\", hv)","sourceCodeStart":611,"sourceCodeEnd":647,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/acl/policy.go#L611-L647","documentation":"Returned by acl.Parse in acl/policy.go:629 when a node_pool stanza lists a capability not accepted by isNodePoolCapabilityValid (acl/policy.go:403). Node pool capabilities are limited to delete, read, write, and deny; everything else — including namespace capabilities — is rejected.","triggerScenarios":"Calling acl.Parse with node_pool { capabilities = [...] } containing values outside {read, write, delete, deny} — misspellings, uppercase, or namespace/variables capabilities pasted in.","commonSituations":"Copy-pasting capability lists between stanza types (namespace vs node_pool vs variables); typos like 'destory' or 'destroy' (variables-only word) in a node_pool stanza; tooling emitting a shared capability enum for all stanza types.","solutions":["Use only read, write, delete, or deny in node_pool capabilities (exact lowercase)","Remove or relocate namespace/variables capabilities into their proper stanzas","Check for typos ('destroy' belongs to variables, not node pools)","Pre-validate each capability against the four-value set before calling Parse"],"exampleFix":"// before\nnode_pool \"prod\" {\n  capabilities = [\"read\", \"destroy\"]\n}\n// after\nnode_pool \"prod\" {\n  capabilities = [\"read\", \"delete\"]\n}","handlingStrategy":"validation","validationCode":"var validPoolCaps = map[string]bool{\"read\": true, \"write\": true, \"delete\": true, \"deny\": true}\nfor _, np := range policy.NodePools {\n    for _, c := range np.Capabilities {\n        if !validPoolCaps[c] { return fmt.Errorf(\"node pool %s: bad capability %q\", np.Name, c) }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Restrict node_pool capabilities to read/write/delete/deny only","Keep per-stanza capability enums distinct; never reuse namespace capability lists","Add CI linting that checks each stanza type against its own allowed capability set"],"tags":["nomad","acl","node-pool","capabilities"],"backgroundTag":"acl-policy-validation-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"}