{"record":{"id":"a681be33ab795351","repo":"ory/hydra","slug":"token-is-not-valid-yet","errorCode":null,"errorMessage":"Token is not valid yet","messagePattern":"Token is not valid yet","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fosite/token/jwt/map_claims.go","lineNumber":125,"sourceCode":"// There is no accounting for clock skew.\n// As well, if any of the above claims are not in the token, it will still\n// be considered a valid claim.\nfunc (m MapClaims) Valid() error {\n\tvErr := new(ValidationError)\n\tnow := TimeFunc().Unix()\n\n\tif !m.VerifyExpiresAt(now, false) {\n\t\tvErr.Inner = errors.New(\"Token is expired\")\n\t\tvErr.Errors |= ValidationErrorExpired\n\t}\n\n\tif !m.VerifyIssuedAt(now, false) {\n\t\tvErr.Inner = errors.New(\"Token used before issued\")\n\t\tvErr.Errors |= ValidationErrorIssuedAt\n\t}\n\n\tif !m.VerifyNotBefore(now, false) {\n\t\tvErr.Inner = errors.New(\"Token is not valid yet\")\n\t\tvErr.Errors |= ValidationErrorNotValidYet\n\t}\n\n\tif vErr.valid() {\n\t\treturn nil\n\t}\n\n\treturn vErr\n}\n\nfunc (m MapClaims) UnmarshalJSON(b []byte) error {\n\t// This custom unmarshal allows to configure the\n\t// go-jose decoding settings since there is no other way\n\t// see https://github.com/square/go-jose/issues/353.\n\t// If issue is closed with a better solution\n\t// this custom Unmarshal method can be removed\n\td := jjson.NewDecoder(bytes.NewReader(b))\n\tmp := map[string]interface{}(m)","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/ory/hydra/blob/4174065ffb052799890f7480f5360a877a67ffc1/fosite/token/jwt/map_claims.go#L107-L143","documentation":"This error is raised by fosite's jwt claims validation when `VerifyNotBefore(now, false)` fails, i.e. the token's `nbf` (not-before) claim lies in the future. The token is structurally fine but its validity window has not opened yet. The library enforces `nbf` to honor the issuer's declared start of validity.","triggerScenarios":"Parsing (via ParseWithClaims -> claims.Valid()) a JWT whose `nbf` claim is greater than the current verifier time — e.g. a token minted with a future `nbf`, or verifier clock behind issuer clock.","commonSituations":"Clock skew between services; tokens minted with `nbf` computed in the wrong timezone or with seconds-vs-milliseconds confusion; long-lived pre-provisioned tokens with a future start date; test tokens hand-built with an erroneous `nbf`.","solutions":["Sync clocks (NTP) between issuer and verifier.","Wait until the token's `nbf` time passes, or re-request a token now.","Fix token generation so `nbf` is set to now (or omitted).","Use leeway in validation (allow a small skew margin before rejecting)."],"exampleFix":"// before\nnbf := time.Now().Add(24 * time.Hour).Unix() // wrong: validity starts tomorrow\nclaims.Set(\"nbf\", nbf)\n// after\nclaims.Set(\"nbf\", time.Now().Unix())","handlingStrategy":"validation","validationCode":"// check nbf before use\nfunc notBeforePassed(claims jwt.MapClaims, leeway time.Duration) bool {\n    nbf, ok := claims[\"nbf\"].(float64)\n    return !ok || time.Now().Add(leeway).After(time.Unix(int64(nbf), 0))\n}","typeGuard":null,"tryCatchPattern":"// detect not-valid-yet specifically and retry later\nvar vErr *jwt.ValidationError\nif errors.As(err, &vErr) && vErr.Errors&jwt.ValidationErrorNotValidYet != 0 {\n    return retryAfter(nbfTime.Sub(time.Now()) + leeway)\n}","preventionTips":["Set nbf to now (or omit it) when minting tokens","Sync clocks between issuer and verifier","Be careful with second-vs-millisecond timestamps when computing nbf","Schedule pre-provisioned tokens with realistic start times"],"tags":["jwt","oauth2","clock-skew","validation"],"backgroundTag":"jwt-token-not-yet-valid","analyzedSha":"4174065ffb052799890f7480f5360a877a67ffc1","analyzedAt":"2026-09-03T14:52:41.581Z","contentChangedAt":"2026-09-03T14:52:41.581Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}