hashicorp/nomad · error
ACL policy not found
Error message
ACL policy not found
What it means
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.
Source
Thrown at nomad/state/state_store_acl.go:172
}
// Insert the role into the table.
if err := txn.Insert(TableACLRoles, role); err != nil {
return false, fmt.Errorf("ACL role insert failed: %v", err)
}
return true, nil
}
// validateACLRolePolicyLinksTxn is the same as ValidateACLRolePolicyLinks but
// allows callers to pass their own transaction.
func (s *StateStore) validateACLRolePolicyLinksTxn(txn *txn, role *structs.ACLRole) error {
for _, policyLink := range role.Policies {
_, existing, err := txn.FirstWatch("acl_policy", indexID, policyLink.Name)
if err != nil {
return fmt.Errorf("ACL policy lookup failed: %v", err)
}
if existing == nil {
return errors.New("ACL policy not found")
}
}
return nil
}
// DeleteACLRolesByID is responsible for batch deleting ACL roles based on
// their ID. It uses a single write transaction for efficiency, however, any
// error means no entries will be committed. An error is produced if a role is
// not found within state which has been passed within the array.
func (s *StateStore) DeleteACLRolesByID(
msgType structs.MessageType, index uint64, roleIDs []string) error {
txn := s.db.WriteTxnMsgT(msgType, index)
defer txn.Abort()
for _, roleID := range roleIDs {
if err := s.deleteACLRoleByIDTxn(txn, roleID); err != nil {
return errView on GitHub (pinned to 482b49bf1a)
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.
Example fix
// before: role references policy that does not exist nomad acl role create -name dev -policy developer-pol // typo // after: create the policy (correct name) first, then the role nomad acl policy apply developer policies/developer.hcl nomad acl role create -name dev -policy developer
Defensive patterns
Strategy: validation
Validate before calling
// before applying the role, check each policy link exists
for _, p := range role.Policies {
if _, _, err := client.ACLPolicies().Get(p.Name, nil); err != nil {
return fmt.Errorf("policy %q must be created before role: %w", p.Name, err)
}
} Try / catch
// treat as preconditions-failed on upsert
if err := upsertRole(role); err != nil && strings.Contains(err.Error(), "ACL policy not found") {
return fmt.Errorf("role %s links unknown policy; create policies first: %w", role.Name, err)
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- ACL role not found
- detected corrupted token within the state store: missing rol
- policy lookup failed: %v
- upserting policy failed: %v
- deleting acl policy failed: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/438ad6b355d0eca4.
Report an issue: GitHub.