{"record":{"id":"1b136cd0f7e475ca","repo":"hashicorp/nomad","slug":"invalid-operator-policy-v","errorCode":null,"errorMessage":"Invalid operator policy: %#v","messagePattern":"Invalid operator policy: %#v","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"acl/policy.go","lineNumber":688,"sourceCode":"\t// with the unknown keys.\n\tif len(p.ExtraKeysHCL) > 0 && strict {\n\t\treturn nil, fmt.Errorf(\"Invalid or duplicate policy keys: %v\",\n\t\t\tstrings.Join(p.ExtraKeysHCL, \", \"))\n\t}\n\n\tp.ExtraKeysHCL = nil\n\n\tif p.Agent != nil && !isPolicyValid(p.Agent.Policy) {\n\t\treturn nil, fmt.Errorf(\"Invalid agent policy: %#v\", p.Agent)\n\t}\n\n\tif p.Node != nil && !isPolicyValid(p.Node.Policy) {\n\t\treturn nil, fmt.Errorf(\"Invalid node policy: %#v\", p.Node)\n\t}\n\n\tif p.Operator != nil {\n\t\tif p.Operator.Policy != \"\" && !isPolicyValid(p.Operator.Policy) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid operator policy: %#v\", p.Operator)\n\t\t}\n\t\tfor _, cap := range p.Operator.Capabilities {\n\t\t\tif !isOperatorCapabilityValid(cap) {\n\t\t\t\treturn nil, fmt.Errorf(\"Invalid operator capability '%s'\", cap)\n\t\t\t}\n\t\t}\n\n\t\t// Expand the short hand policy to the capabilities and\n\t\t// add to any existing capabilities\n\t\tif p.Operator.Policy != \"\" {\n\t\t\textraCap := expandOperatorPolicy(p.Operator.Policy)\n\t\t\tp.Operator.Capabilities = append(p.Operator.Capabilities, extraCap...)\n\t\t}\n\t}\n\n\tif p.Sentinel != nil {\n\t\tif p.Sentinel.Policy != \"\" && !isPolicyValid(p.Sentinel.Policy) {\n\t\t\treturn nil, fmt.Errorf(\"Invalid sentinel policy: %#v\", p.Sentinel)","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/acl/policy.go#L670-L706","documentation":"acl.Parse (acl/policy.go:688) validates the top-level operator block's Policy string with isPolicyValid, which only accepts deny, read, write, scale. A non-empty operator policy with any other value fails parsing and rejects the whole policy. The OperatorPolicy struct is printed via %#v.","triggerScenarios":"acl.Parse on a policy containing operator { policy = \"<invalid>\" } where the value is not exactly \"deny\", \"read\", \"write\", or \"scale\" — e.g. 'snapshot', 'keyring', or an empty quoted string.","commonSituations":"Putting operator capability names (snapshot-save, keyring-rotate, etc.) into the policy field instead of the capabilities list; case mistakes; template generation mixing policy and capability vocabularies.","solutions":["Set policy to one of exactly: deny, read, write, scale (lowercase).","For granular operator permissions, drop policy and use capabilities = [\"snapshot-save\", \"keyring-read\", ...] instead.","Check the %#v struct in the error to identify the bad value."],"exampleFix":"// before\noperator {\n  policy = \"snapshot\"\n}\n// after\noperator {\n  capabilities = [\"snapshot-save\"]\n}","handlingStrategy":"validation","validationCode":"if policy.Operator != nil && policy.Operator.Policy != \"\" &&\n    !map[string]bool{\"deny\": true, \"read\": true, \"write\": true, \"scale\": true}[policy.Operator.Policy] {\n    return fmt.Errorf(\"operator policy %q must be deny|read|write|scale\", policy.Operator.Policy)\n}","typeGuard":"func isPolicyValid(policy string) bool {\n    switch policy {\n    case \"deny\", \"read\", \"write\", \"scale\":\n        return true\n    }\n    return false\n}","tryCatchPattern":"_, err := acl.Parse(rules, acl.PolicyParseStrict)\nif err != nil {\n    if strings.Contains(err.Error(), \"Invalid operator policy\") {\n        // replace the operator shorthand policy or switch to capabilities\n    }\n    return err\n}","preventionTips":["Use only deny, read, write, scale in the operator policy field","Put operation-specific grants (snapshot-save, keyring-*) in capabilities, not policy","Keep policy-generator tooling aligned with the canonical shorthand constants"],"tags":["nomad","acl","policy-validation","operator"],"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"}