hashicorp/nomad · error
expiration time cannot be less than %s in the future (was %s
Error message
expiration time cannot be less than %s in the future (was %s)
What it means
Complementing the max check, Validate rejects ExpirationTime values less than minTTL after CreateTime with 'expiration time cannot be less than %s in the future (was %s)'. This prevents creating tokens that are effectively already expired or expire within an unusably short window.
Source
Thrown at nomad/structs/acl.go:806
}
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
- Set ExpirationTime at least minTTL after CreateTime (check the server's min TTL config).
- Use token deletion (`nomad acl token delete`) for immediate revocation instead of near-term expiry.
- Synchronize clocks (NTP) between the client constructing the token and the Nomad servers.
- Compute expiry with CreateTime from the server response rather than local time to avoid skew.
Example fix
// before exp := token.CreateTime // expires immediately // after minTTL := 5 * time.Minute exp := token.CreateTime.Add(minTTL) token.ExpirationTime = &exp
Defensive patterns
Strategy: validation
Validate before calling
func validateMinExpiry(createTime, expiry time.Time, minTTL time.Duration) error {
if expiry.Sub(createTime) < minTTL {
return fmt.Errorf("expiry %s is below min TTL %s", expiry, minTTL)
}
return nil
} Try / catch
if err := token.Validate(nil); err != nil {
if strings.Contains(err.Error(), "cannot be less than") {
exp := token.CreateTime.Add(minTokenTTL)
token.ExpirationTime = &exp
return token.Validate(nil)
}
return err
} Prevention
- Compute expiry from the server-reported CreateTime, not local clocks, to absorb clock skew.
- Use token delete for immediate revocation rather than near-past expiry times.
- Ensure NTP-synced clocks on hosts issuing tokens.
- Clamp requested TTLs to [minTTL, maxTTL] before calling the API.
When it happens
Trigger: Creating an ACL token whose ExpirationTime is sooner than minTTL after CreateTime — e.g. expiry equal to create time, an expiry in the past, or a TTL below the server-configured minimum.
Common situations: Tests or scripts computing expiry from a stale clock (client/server clock skew); accidentally passing create time as expiry; timezone/UTC conversion errors shifting the timestamp backwards; very short TTLs intended as immediate revocation instead of using token delete.
Related errors
- expiration time cannot be more 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/10c08ddcb6739b3f.
Report an issue: GitHub.