{"record":{"id":"c22894cd2277e2e7","repo":"hashicorp/nomad","slug":"expiration-time-cannot-be-before-create-time","errorCode":null,"errorMessage":"expiration time cannot be before create time","messagePattern":"expiration time cannot be before create time","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/acl.go","lineNumber":793,"sourceCode":"\t\t\tmErr.Errors = append(mErr.Errors, errors.New(\"management token cannot be associated with policies or roles\"))\n\t\t}\n\tdefault:\n\t\tmErr.Errors = append(mErr.Errors, errors.New(\"token type must be client or management\"))\n\t}\n\n\t// There are different validation rules depending on whether the ACL token\n\t// is being created or updated.\n\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 {","sourceCodeStart":775,"sourceCodeEnd":811,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/acl.go#L775-L811","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ntoken.ExpirationTime = &pastTime\ntoken.CreateTime = now\n// after\nexp := time.Now().Add(24 * time.Hour)\ntoken.ExpirationTime = &exp","handlingStrategy":"validation","validationCode":"if token.ExpirationTime != nil && !token.ExpirationTime.IsZero() && token.CreateTime.After(*token.ExpirationTime) {\n    return errors.New(\"expiration time must be after create time\")\n}","typeGuard":"func expirationAfterCreate(t *structs.ACLToken) bool {\n    return t.ExpirationTime == nil || t.ExpirationTime.IsZero() || t.CreateTime.Before(*t.ExpirationTime)\n}","tryCatchPattern":"if err := token.Validate(minTTL, maxTTL, nil); err != nil {\n    if strings.Contains(err.Error(), \"expiration time cannot be before create time\") {\n        exp := time.Now().Add(24 * time.Hour)\n        token.ExpirationTime = &exp\n        err = token.Validate(minTTL, maxTTL, nil)\n    }\n}","preventionTips":["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"],"tags":["nomad","acl","token","expiration","validation"],"backgroundTag":"invalid-time-range","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"}