{"record":{"id":"33a3b3338d0227d2","repo":"hashicorp/nomad","slug":"expiration-time-cannot-be-more-than-s-in-the-futu","errorCode":null,"errorMessage":"expiration time cannot be more than %s in the future (was %s)","messagePattern":"expiration time cannot be more than (.+?) in the future \\(was (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":801,"sourceCode":"\tswitch existing {\n\tcase nil:\n\t\tif a.ExpirationTTL < 0 {\n\t\t\tmErr.Errors = append(mErr.Errors,\n\t\t\t\tfmt.Errorf(\"token expiration TTL '%s' should not be negative\", a.ExpirationTTL))\n\t\t}\n\n\t\tif a.ExpirationTime != nil && !a.ExpirationTime.IsZero() {\n\n\t\t\tif a.CreateTime.After(*a.ExpirationTime) {\n\t\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"expiration time cannot be before create time\"))\n\t\t\t}\n\n\t\t\t// Create a time duration which details the time-til-expiry, so we can\n\t\t\t// check this against the regions max and min values.\n\t\t\texpiresIn := a.ExpirationTime.Sub(a.CreateTime)\n\t\t\tif expiresIn > maxTTL {\n\t\t\t\tmErr.Errors = append(mErr.Errors,\n\t\t\t\t\tfmt.Errorf(\"expiration time cannot be more than %s in the future (was %s)\",\n\t\t\t\t\t\tmaxTTL, expiresIn))\n\n\t\t\t} else if expiresIn < minTTL {\n\t\t\t\tmErr.Errors = append(mErr.Errors,\n\t\t\t\t\tfmt.Errorf(\"expiration time cannot be less than %s in the future (was %s)\",\n\t\t\t\t\t\tminTTL, expiresIn))\n\t\t\t}\n\t\t}\n\tdefault:\n\t\tif existing.Global != a.Global {\n\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"cannot toggle global mode\"))\n\t\t}\n\t\tif existing.ExpirationTTL != a.ExpirationTTL {\n\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"cannot update expiration TTL\"))\n\t\t}\n\t\tif a.ExpirationTime != nil {\n\t\t\tif !existing.ExpirationTime.Equal(*a.ExpirationTime) {\n\t\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"cannot update expiration time\"))","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L783-L819","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ntoken.ExpirationTime = &farFuture // e.g. now + 8760h\n// after\nmaxTTL := 24 * time.Hour\ntoken.ExpirationTime = &expiredIn\nexpiredIn := token.CreateTime.Add(maxTTL)","handlingStrategy":"validation","validationCode":"func validateExpiry(createTime, expiry time.Time, maxTTL time.Duration) error {\n    if expiry.Sub(createTime) > maxTTL {\n        return fmt.Errorf(\"expiry %s exceeds max TTL %s\", expiry, maxTTL)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := token.Validate(nil); err != nil {\n    if strings.Contains(err.Error(), \"cannot be more than\") {\n        max := token.CreateTime.Add(maxTokenTTL)\n        token.ExpirationTime = &max\n        return token.Validate(nil)\n    }\n    return err\n}","preventionTips":["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."],"tags":["acl","token","validation","ttl","expiration"],"backgroundTag":"acl-token-ttl-invalid","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}