hashicorp/nomad · error

ACL role not found

Error message

ACL role not found

What it means

deleteACLRoleByIDTxn verifies that an ACL role with the given ID exists in the acl_roles table before deleting it. If the unique index lookup returns nil, it returns "ACL role not found" instead of deleting, making the delete operation strict (no silent no-op). The error propagates out of DeleteACLRolesByID to the RPC caller.

Source

Thrown at nomad/state/state_store_acl.go:212

	// Update the index table to indicate an update has occurred.
	if err := txn.Insert(tableIndex, &IndexEntry{TableACLRoles, index}); err != nil {
		return fmt.Errorf("index update failed: %v", err)
	}

	return txn.Commit()
}

// deleteACLRoleByIDTxn deletes a single ACL role from the state store using the
// provided write transaction. It is the responsibility of the caller to update
// the index table.
func (s *StateStore) deleteACLRoleByIDTxn(txn *txn, roleID string) error {

	existing, err := txn.First(TableACLRoles, indexID, roleID)
	if err != nil {
		return fmt.Errorf("ACL role lookup failed: %v", err)
	}
	if existing == nil {
		return errors.New("ACL role not found")
	}

	// Delete the existing entry from the table.
	if err := txn.Delete(TableACLRoles, existing); err != nil {
		return fmt.Errorf("ACL role deletion failed: %v", err)
	}
	return nil
}

// GetACLRoles returns an iterator that contains all ACL roles stored within
// state.
func (s *StateStore) GetACLRoles(ws memdb.WatchSet) (memdb.ResultIterator, error) {
	txn := s.db.ReadTxn()

	// Walk the entire table to get all ACL roles.
	iter, err := txn.Get(TableACLRoles, indexID)
	if err != nil {
		return nil, fmt.Errorf("ACL role lookup failed: %v", err)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. List roles (nomad acl role list) and confirm the exact ID before deleting.
  2. Treat this error as idempotent success in automation if the desired end state is 'role gone'.
  3. Re-fetch the role ID from the authoritative cluster/namespace; UUIDs are not portable across clusters.

Example fix

// before: blind delete
client.ACLRoles().Delete(roleID, nil)
// after: check existence first or tolerate not-found
_, _, err := client.ACLRoles().Get(roleID, nil)
if err == nil {
    _, err = client.ACLRoles().Delete(roleID, nil)
}
Defensive patterns

Strategy: validation

Validate before calling

_, _, err := client.ACLRoles().Get(roleID, nil)
if err != nil {
    return fmt.Errorf("role %s does not exist, skipping delete", roleID)
}

Try / catch

err := client.ACLRoles().Delete(roleID, nil)
if err != nil && strings.Contains(err.Error(), "ACL role not found") {
    return nil // already deleted
}

Prevention

When it happens

Trigger: ACLRole.Delete RPC (nomad acl role delete <id>) with a role ID that is absent from the state store; concurrent double-delete where a second request races the first; deleting by stale ID after the role was already removed.

Common situations: Scripts re-running a deletion; deleting a role on a region/agent that never had it; stale IDs cached from a previous cluster; typo'd UUID in automation.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/b1f505afc9f3e024. Report an issue: GitHub.