{"record":{"id":"438ad6b355d0eca4","repo":"hashicorp/nomad","slug":"acl-policy-not-found","errorCode":null,"errorMessage":"ACL policy not found","messagePattern":"ACL policy not found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/state/state_store_acl.go","lineNumber":172,"sourceCode":"\t}\n\n\t// Insert the role into the table.\n\tif err := txn.Insert(TableACLRoles, role); err != nil {\n\t\treturn false, fmt.Errorf(\"ACL role insert failed: %v\", err)\n\t}\n\treturn true, nil\n}\n\n// validateACLRolePolicyLinksTxn is the same as ValidateACLRolePolicyLinks but\n// allows callers to pass their own transaction.\nfunc (s *StateStore) validateACLRolePolicyLinksTxn(txn *txn, role *structs.ACLRole) error {\n\tfor _, policyLink := range role.Policies {\n\t\t_, existing, err := txn.FirstWatch(\"acl_policy\", indexID, policyLink.Name)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"ACL policy lookup failed: %v\", err)\n\t\t}\n\t\tif existing == nil {\n\t\t\treturn errors.New(\"ACL policy not found\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// DeleteACLRolesByID is responsible for batch deleting ACL roles based on\n// their ID. It uses a single write transaction for efficiency, however, any\n// error means no entries will be committed. An error is produced if a role is\n// not found within state which has been passed within the array.\nfunc (s *StateStore) DeleteACLRolesByID(\n\tmsgType structs.MessageType, index uint64, roleIDs []string) error {\n\n\ttxn := s.db.WriteTxnMsgT(msgType, index)\n\tdefer txn.Abort()\n\n\tfor _, roleID := range roleIDs {\n\t\tif err := s.deleteACLRoleByIDTxn(txn, roleID); err != nil {\n\t\t\treturn err","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/state/state_store_acl.go#L154-L190","documentation":"Nomad's state store rejects an ACL role upsert when one of the role's policy links references a policy name that does not exist in the acl_policy table. validateACLRolePolicyLinksTxn iterates role.Policies and does a txn.FirstWatch lookup per link; a nil result means the referenced policy is absent. The write is aborted so roles can never point at nonexistent policies.","triggerScenarios":"Calling the ACL Role Upsert RPC (ACLPolicy.UpsertACLRoles / nomad acl role apply with -policy) where role.Policies contains a name with no matching ACL policy, or restoring a snapshot whose role records predate their policy records.","commonSituations":"Typo in the policy name in an HCL/JSON role definition; creating a role before its policies; deleting a policy that a role still links to and then re-applying the role; state restore from an older cluster.","solutions":["Create the missing ACL policy first (nomad acl policy apply) so the name in role.Policies matches exactly.","Fix the typo in the role's policy link name and re-apply the role.","Remove the stale policy link from the role if the policy is intentionally gone.","If restoring state, ensure policies are restored before roles."],"exampleFix":"// before: role references policy that does not exist\nnomad acl role create -name dev -policy developer-pol  // typo\n// after: create the policy (correct name) first, then the role\nnomad acl policy apply developer policies/developer.hcl\nnomad acl role create -name dev -policy developer","handlingStrategy":"validation","validationCode":"// before applying the role, check each policy link exists\nfor _, p := range role.Policies {\n    if _, _, err := client.ACLPolicies().Get(p.Name, nil); err != nil {\n        return fmt.Errorf(\"policy %q must be created before role: %w\", p.Name, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"// treat as preconditions-failed on upsert\nif err := upsertRole(role); err != nil && strings.Contains(err.Error(), \"ACL policy not found\") {\n    return fmt.Errorf(\"role %s links unknown policy; create policies first: %w\", role.Name, err)\n}","preventionTips":["Always provision ACL policies before roles in IaC (explicit depends_on in Terraform).","Generate role definitions from `nomad acl policy list` output instead of hand-typing names.","Add a CI lint step that validates policy references in role manifests."],"tags":["nomad","acl","state-store","referential-integrity"],"backgroundTag":"referenced-resource-not-found","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"}