hashicorp/nomad · error
expiration time cannot be before create time
Error message
expiration time cannot be before create time
What it means
A validation error from ACLToken.Validate: when a token specifies an ExpirationTime, that time must be after the token's CreateTime. Nomad rejects tokens whose expiration predates creation because they would be born already expired.
Source
Thrown at nomad/structs/acl.go:793
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))
}
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 {View on GitHub (pinned to 482b49bf1a)
Solutions
- Set ExpirationTime strictly after CreateTime (use server time or add a TTL buffer)
- Prefer setting ExpirationTTL and let the server compute the absolute time
- Synchronize clocks (NTP) on hosts generating expiration timestamps
Example fix
// before token.ExpirationTime = &pastTime token.CreateTime = now // after exp := time.Now().Add(24 * time.Hour) token.ExpirationTime = &exp
Defensive patterns
Strategy: validation
Validate before calling
if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() && token.CreateTime.After(*token.ExpirationTime) {
return errors.New("expiration time must be after create time")
} Type guard
func expirationAfterCreate(t *structs.ACLToken) bool {
return t.ExpirationTime == nil || t.ExpirationTime.IsZero() || t.CreateTime.Before(*t.ExpirationTime)
} Try / catch
if err := token.Validate(minTTL, maxTTL, nil); err != nil {
if strings.Contains(err.Error(), "expiration time cannot be before create time") {
exp := time.Now().Add(24 * time.Hour)
token.ExpirationTime = &exp
err = token.Validate(minTTL, maxTTL, nil)
}
} Prevention
- Compute expiration from the current time plus a TTL, not stored constants
- Use ExpirationTTL and let the server derive the absolute time
- Keep hosts NTP-synchronized to avoid clock-skew regressions
When it happens
Trigger: Creating/updating an ACLToken where CreateTime.After(*ExpirationTime) is true — e.g. an expiration computed from a clock-skewed host, or copying a token's expiration while resetting CreateTime to now.
Common situations: Clock skew between the client computing the expiration and the server validating create time; template-based token generation using a stale/past timestamp; tests with hardcoded times.
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
- cannot toggle global mode
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c22894cd2277e2e7.
Report an issue: GitHub.