hashicorp/nomad · error
expiration time cannot be more than %s in the future (was %s
Error message
expiration time cannot be more than %s in the future (was %s)
What it means
When creating a token with an explicit ExpirationTime, Validate computes expiresIn = ExpirationTime - CreateTime and rejects values greater than the region's maxTTL with 'expiration time cannot be more than %s in the future (was %s)'. This caps how far ahead tokens may be scheduled to expire.
Source
Thrown at nomad/structs/acl.go:801
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))
}
if a.ExpirationTime != nil && !a.ExpirationTime.IsZero() {
if a.CreateTime.After(*a.ExpirationTime) {
mErr.Errors = append(mErr.Errors, errors.New("expiration time cannot be before create time"))
}
// Create a time duration which details the time-til-expiry, so we can
// check this against the regions max and min values.
expiresIn := a.ExpirationTime.Sub(a.CreateTime)
if expiresIn > maxTTL {
mErr.Errors = append(mErr.Errors,
fmt.Errorf("expiration time cannot be more than %s in the future (was %s)",
maxTTL, expiresIn))
} else if expiresIn < minTTL {
mErr.Errors = append(mErr.Errors,
fmt.Errorf("expiration time cannot be less than %s in the future (was %s)",
minTTL, expiresIn))
}
}
default:
if existing.Global != a.Global {
mErr.Errors = append(mErr.Errors, errors.New("cannot toggle global mode"))
}
if existing.ExpirationTTL != a.ExpirationTTL {
mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration TTL"))
}
if a.ExpirationTime != nil {
if !existing.ExpirationTime.Equal(*a.ExpirationTime) {
mErr.Errors = append(mErr.Errors, errors.New("cannot update expiration time"))View on GitHub (pinned to 482b49bf1a)
Solutions
- Shorten ExpirationTime to within maxTTL of the token create time.
- If a longer life is required, plan token rotation (recreate before expiry) instead of a single long TTL.
- Check the server's ttl config (agent ACL block) and either raise max_token_ttl consciously or align token policy with it.
- Compute expiry as CreateTime.Add(allowedTTL) rather than absolute dates to stay in bounds.
Example fix
// before token.ExpirationTime = &farFuture // e.g. now + 8760h // after maxTTL := 24 * time.Hour token.ExpirationTime = &expiredIn expiredIn := token.CreateTime.Add(maxTTL)
Defensive patterns
Strategy: validation
Validate before calling
func validateExpiry(createTime, expiry time.Time, maxTTL time.Duration) error {
if expiry.Sub(createTime) > maxTTL {
return fmt.Errorf("expiry %s exceeds max TTL %s", expiry, maxTTL)
}
return nil
} Try / catch
if err := token.Validate(nil); err != nil {
if strings.Contains(err.Error(), "cannot be more than") {
max := token.CreateTime.Add(maxTokenTTL)
token.ExpirationTime = &max
return token.Validate(nil)
}
return err
} Prevention
- Derive ExpirationTime from CreateTime.Add(ttl) rather than fixed calendar dates.
- Keep cluster max_token_ttl config and token-issuing tooling in sync.
- Implement rotation: short TTLs renewed before expiry, instead of one long TTL.
- Re-validate tokens when promoting them across regions with different TTL limits.
When it happens
Trigger: Creating an ACL token whose ExpirationTime is more than maxTTL after CreateTime — e.g. requesting a 1-year expiry when maxTTL is 24h, or a server whose max_accessor_ttl/max_token_ttl configuration clamps maxTTL below the requested expiry.
Common situations: Long-lived service tokens created after an operator tightened the cluster's max TTL via server config; cross-region tokens moved to a region with stricter limits; scripts hardcoding far-future expiry dates.
Related errors
- expiration time cannot be less than %s in the future (was %s
- expiration time cannot be before create time
- token expiration TTL '%s' should not be negative
- token name too long
- client token missing policies or roles
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/33a3b3338d0227d2.
Report an issue: GitHub.