{"record":{"id":"10c08ddcb6739b3f","repo":"hashicorp/nomad","slug":"expiration-time-cannot-be-less-than-s-in-the-futu","errorCode":null,"errorMessage":"expiration time cannot be less than %s in the future (was %s)","messagePattern":"expiration time cannot be less than (.+?) in the future \\(was (.+?)\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":806,"sourceCode":"\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\"))\n\t\t\t}\n\t\t}\n\n\t}\n","sourceCodeStart":788,"sourceCodeEnd":824,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L788-L824","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nexp := token.CreateTime // expires immediately\n// after\nminTTL := 5 * time.Minute\nexp := token.CreateTime.Add(minTTL)\ntoken.ExpirationTime = &exp","handlingStrategy":"validation","validationCode":"func validateMinExpiry(createTime, expiry time.Time, minTTL time.Duration) error {\n    if expiry.Sub(createTime) < minTTL {\n        return fmt.Errorf(\"expiry %s is below min TTL %s\", expiry, minTTL)\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := token.Validate(nil); err != nil {\n    if strings.Contains(err.Error(), \"cannot be less than\") {\n        exp := token.CreateTime.Add(minTokenTTL)\n        token.ExpirationTime = &exp\n        return token.Validate(nil)\n    }\n    return err\n}","preventionTips":["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."],"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"}