hashicorp/nomad · error
invalid MaxTokenTTL value '%s' (should be between %s and %s)
Error message
invalid MaxTokenTTL value '%s' (should be between %s and %s)
What it means
ACLAuthMethod.Validate() rejects MaxTokenTTL when it is not within [minTTL, maxTTL], the bounds supplied by the server (system defaults or agent config). Both the error message and bounds are formatted with time.Duration.String().
Source
Thrown at nomad/structs/acl.go:1448
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid name '%s'", a.Name))
}
if !slices.Contains([]string{ACLAuthMethodTokenLocalityLocal, ACLAuthMethodTokenLocalityGlobal}, a.TokenLocality) {
mErr.Errors = append(
mErr.Errors, fmt.Errorf("invalid token locality '%s'", a.TokenLocality))
}
if !slices.Contains(ValidACLAuthMethodTypes, a.Type) {
mErr.Errors = append(
mErr.Errors, fmt.Errorf("invalid token type '%s'", a.Type))
}
if err := a.Config.Validate(a.Type); err != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("invalid config: %w", err))
}
if minTTL > a.MaxTokenTTL || a.MaxTokenTTL > maxTTL {
mErr.Errors = append(mErr.Errors, fmt.Errorf(
"invalid MaxTokenTTL value '%s' (should be between %s and %s)",
a.MaxTokenTTL.String(), minTTL.String(), maxTTL.String()))
}
return mErr.ErrorOrNil()
}
// Sanitize returns a copy of the ACLAuthMethod with any secrets redacted
func (a *ACLAuthMethod) Sanitize() *ACLAuthMethod {
if a == nil || a.Config == nil {
return a
}
// copy to ensure we do not mutate a pointer pulled directly out of state.
clean := a.Copy()
// clean nested structs here, so it's obvious what all is being cleaned
// in one spot, rather than follow a stack of sanitization calls.
if clean.Config.OIDCClientSecret != "" {
clean.Config.OIDCClientSecret = "redacted"View on GitHub (pinned to 482b49bf1a)
Solutions
- Set MaxTokenTTL between the server's min and max (check agent config: audit the ACL token TTL defaults, e.g. 1m–24h).
- Read the error message bounds and align the value to them.
- Set MaxTokenTTL explicitly instead of relying on zero-value; use time.Duration constants in Go.
- If a longer TTL is genuinely needed, raise the server-side max token TTL config, then re-submit.
Example fix
// before am.MaxTokenTTL = 0 // "no expiry" // after am.MaxTokenTTL = 24 * time.Hour
Defensive patterns
Strategy: validation
Validate before calling
minTTL, maxTTL := 1*time.Minute, 24*time.Hour // confirm with server config
if am.MaxTokenTTL < minTTL || am.MaxTokenTTL > maxTTL {
return fmt.Errorf("MaxTokenTTL %s outside [%s, %s]", am.MaxTokenTTL, minTTL, maxTTL)
} Try / catch
if err := am.Validate(minTTL, maxTTL); err != nil {
if strings.Contains(err.Error(), "invalid MaxTokenTTL") {
// parse bounds from message and clamp before retry
}
return err
} Prevention
- Never use zero MaxTokenTTL expecting 'unlimited'
- Pull the real min/max from the server's ACL config rather than guessing
- Use time.Duration literals; double-check h vs m units
When it happens
Trigger: Upserting an ACL auth method whose MaxTokenTTL is below the server minimum or above the server maximum (e.g. 1s when min is 1m, or 0/8760h when the configured max is 24h). Also triggered when minTTL > MaxTokenTTL with a zero MaxTokenTTL where a nonzero is required.
Common situations: Setting MaxTokenTTL: "0s" expecting 'no expiry'; guessing TTLs in a config where the operator changed the server's default/max ACL token TTLs; unit mistakes like "2h" vs "2m".
Related errors
- token name too long
- client token missing policies or roles
- management token cannot be associated with policies or roles
- token type must be client or management
- expiration time cannot be before create time
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c39a1f81695809ee.
Report an issue: GitHub.