{"record":{"id":"a83e82fdcf4e3360","repo":"hashicorp/nomad","slug":"invalid-or-duplicate-policy-keys-v","errorCode":null,"errorMessage":"Invalid or duplicate policy keys: %v","messagePattern":"Invalid or duplicate policy keys: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"acl/policy.go","lineNumber":672,"sourceCode":"\n\t\t// Expand the short hand policy to the capabilities and\n\t\t// add to any existing capabilities\n\t\tif hv.Policy != \"\" {\n\t\t\textraCap := expandHostVolumePolicy(hv.Policy)\n\t\t\thv.Capabilities = append(hv.Capabilities, extraCap...)\n\t\t}\n\n\t\t// Remove the host-volume name from the extra key list.\n\t\tp.removeExtraKey(hv.Name)\n\t}\n\n\t// Now that we have processed all known keys, return an error if the\n\t// operator wrote a policy with unknown keys if we are being strict. While\n\t// these do not grant any extra privileges, it can be misleaing to allow\n\t// these and cause problems later if we add new capabilities that collide\n\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 {","sourceCodeStart":654,"sourceCodeEnd":690,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/acl/policy.go#L654-L690","documentation":"In strict mode (PolicyParseStrict, used when creating/updating ACL policies), acl.Parse (acl/policy.go:672) rejects policies whose HCL contained keys that are not recognized — captured in ExtraKeysHCL. This catches typos and duplicate blocks that the HCL parser folds into unused keys. The same policy may still parse in lenient mode (PolicyParseLenient) for evaluation, but cannot be written.","triggerScenarios":"POST/PUT of an ACL policy via the Nomad API or 'nomad acl policy apply' where the HCL/JSON has a misspelled top-level key (e.g. 'namespaces' instead of 'namespace'), an unknown key, or repeated blocks of the same type (multiple namespace/host_volume/node_pool blocks trigger leftover keys if the dedup in Parse misses them).","commonSituations":"Typo'd block names like 'host_volumes' or 'nodepool'; copy-pasted JSON policies with extra fields; old policies written before a schema change being re-applied under strict validation; duplicate host_volume blocks accidentally concatenated in a rendered template.","solutions":["The error lists the offending keys — remove or rename each one so it matches a known block (namespace, node_pool, host_volume, agent, node, operator, sentinel, quota, plugin).","Look for misspellings or plural forms of block names (e.g. 'namespaces' → 'namespace').","Check for duplicated blocks of the same type in generated/merged policy files and keep a single instance.","If you are only evaluating an existing policy (not writing), use acl.Parse(rules, PolicyParseLenient) instead of strict mode."],"exampleFix":"// before\nnamespaces {\n  name = \"default\"\n  policy = \"write\"\n}\n// after\nnamespace \"default\" {\n  policy = \"write\"\n}","handlingStrategy":"validation","validationCode":"knownKeys := []string{\"namespace\", \"node_pool\", \"host_volume\", \"agent\", \"node\", \"operator\", \"sentinel\", \"quota\", \"plugin\"}\nfor _, line := range strings.Split(rules, \"\\n\") {\n    if m := regexp.MustCompile(`^\\s*([a-z_]+)\\s*\\{`).FindStringSubmatch(line); m != nil && !slices.Contains(knownKeys, m[1]) {\n        return fmt.Errorf(\"unknown policy block %q\", m[1])\n    }\n}","typeGuard":null,"tryCatchPattern":"_, err := acl.Parse(rules, acl.PolicyParseStrict)\nif err != nil {\n    if strings.Contains(err.Error(), \"Invalid or duplicate policy keys\") {\n        // the message lists the offending keys; remove/rename them in the HCL\n    }\n    return err\n}","preventionTips":["Spell block names exactly: namespace, node_pool, host_volume, agent, node, operator, sentinel, quota, plugin (singular forms)","Check for duplicated blocks when merging generated policy files","Dry-run every policy with strict parse before applying to production"],"tags":["nomad","acl","hcl","strict-parsing","unknown-keys"],"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"}