hashicorp/nomad · error

client token missing policies or roles

Error message

client token missing policies or roles

What it means

A validation error from ACLToken.Validate: a client-type ACL token must be associated with at least one policy or one role. A token of type client with empty Policies and empty Roles is rejected at create/update time because it would grant nothing useful (or ambiguously nothing).

Source

Thrown at nomad/structs/acl.go:771

	}
}

// Validate is used to check a token for reasonableness
func (a *ACLToken) Validate(minTTL, maxTTL time.Duration, existing *ACLToken) error {
	var mErr multierror.Error

	// The human friendly name of an ACL token cannot exceed 256 characters.
	if len(a.Name) > maxTokenNameLength {
		mErr.Errors = append(mErr.Errors, errors.New("token name too long"))
	}

	// The type of an ACL token must be set. An ACL token of type client must
	// have associated policies or roles, whereas a management token cannot be
	// associated with policies.
	switch a.Type {
	case ACLClientToken:
		if len(a.Policies) == 0 && len(a.Roles) == 0 {
			mErr.Errors = append(mErr.Errors, errors.New("client token missing policies or roles"))
		}
	case ACLManagementToken:
		if len(a.Policies) != 0 || len(a.Roles) != 0 {
			mErr.Errors = append(mErr.Errors, errors.New("management token cannot be associated with policies or roles"))
		}
	default:
		mErr.Errors = append(mErr.Errors, errors.New("token type must be client or management"))
	}

	// There are different validation rules depending on whether the ACL token
	// is being created or updated.
	switch existing {
	case nil:
		if a.ExpirationTTL < 0 {
			mErr.Errors = append(mErr.Errors,
				fmt.Errorf("token expiration TTL '%s' should not be negative", a.ExpirationTTL))
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Attach at least one policy name to the token's Policies list
  2. Or assign at least one role via the Roles list
  3. Use Type "management" only if unrestricted access is actually intended
  4. Validate client tokens carry policies or roles before calling the API

Example fix

// before
token := &api.ACLToken{Name: "ci", Type: "client"}
// after
token := &api.ACLToken{Name: "ci", Type: "client", Policies: []string{"read-only"}}
Defensive patterns

Strategy: validation

Validate before calling

if token.Type == "client" && len(token.Policies) == 0 && len(token.Roles) == 0 {
    return errors.New("client tokens require at least one policy or role")
}

Type guard

func clientTokenHasGrants(t *structs.ACLToken) bool {
    return t.Type != structs.ACLClientToken || len(t.Policies) > 0 || len(t.Roles) > 0
}

Try / catch

if err := token.Validate(minTTL, maxTTL, nil); err != nil {
    if strings.Contains(err.Error(), "client token missing policies or roles") {
        token.Policies = append(token.Policies, "default")
        err = token.Validate(minTTL, maxTTL, nil)
    }
}

Prevention

When it happens

Trigger: Upserting an ACLToken with Type == "client" while len(Policies) == 0 and len(Roles) == 0.

Common situations: Automation creating tokens from templates that forget to attach policies; token creation UI/API where role assignment was skipped; refactors that moved permissions from policies to roles but left both lists empty.

Related errors


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