ory/hydra · error
Token is not valid yet
Error message
Token is not valid yet
What it means
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.
Source
Thrown at fosite/token/jwt/map_claims.go:125
// There is no accounting for clock skew.
// As well, if any of the above claims are not in the token, it will still
// be considered a valid claim.
func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
if !m.VerifyExpiresAt(now, false) {
vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired
}
if !m.VerifyIssuedAt(now, false) {
vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if !m.VerifyNotBefore(now, false) {
vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
func (m MapClaims) UnmarshalJSON(b []byte) error {
// This custom unmarshal allows to configure the
// go-jose decoding settings since there is no other way
// see https://github.com/square/go-jose/issues/353.
// If issue is closed with a better solution
// this custom Unmarshal method can be removed
d := jjson.NewDecoder(bytes.NewReader(b))
mp := map[string]interface{}(m)View on GitHub (pinned to 4174065ffb)
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).
Example fix
// before
nbf := time.Now().Add(24 * time.Hour).Unix() // wrong: validity starts tomorrow
claims.Set("nbf", nbf)
// after
claims.Set("nbf", time.Now().Unix()) Defensive patterns
Strategy: validation
Validate before calling
// check nbf before use
func notBeforePassed(claims jwt.MapClaims, leeway time.Duration) bool {
nbf, ok := claims["nbf"].(float64)
return !ok || time.Now().Add(leeway).After(time.Unix(int64(nbf), 0))
} Try / catch
// detect not-valid-yet specifically and retry later
var vErr *jwt.ValidationError
if errors.As(err, &vErr) && vErr.Errors&jwt.ValidationErrorNotValidYet != 0 {
return retryAfter(nbfTime.Sub(time.Now()) + leeway)
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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`.
Related errors
- Token used before issued
- Token is expired
- Session must be of type JWTSessionContainer but got type: %T
- GetTokenClaims() must not be nil
- device_challenge is required
AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03).
Data as JSON: /api/errors/a681be33ab795351.
Report an issue: GitHub.