kataras/iris · error
refresh max age should be bigger than access token's one[%d
Error message
refresh max age should be bigger than access token's one[%d - %d]
What it means
iris-jwt's Signer.NewTokenPair requires the refresh token's MaxAge (refreshMaxAge) to be strictly greater than the access token's configured MaxAge (s.MaxAge). The guard prevents creating a pair where the refresh token expires before or exactly when the access token does, which would make refresh flows useless. The error message prints both durations.
Source
Thrown at middleware/jwt/signer.go:84
}
// Sign generates a new token based on the given "claims" which is valid up to "s.MaxAge".
func (s *Signer) Sign(claims any, opts ...SignOption) ([]byte, error) {
if len(opts) > 0 {
opts = append(opts, s.Options...)
} else {
opts = s.Options
}
return SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, opts...)
}
// NewTokenPair accepts the access and refresh claims plus the life time duration for the refresh token
// and generates a new token pair which can be sent to the client.
// The same token pair can be json-decoded.
func (s *Signer) NewTokenPair(accessClaims any, refreshClaims any, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {
if refreshMaxAge <= s.MaxAge {
return TokenPair{}, fmt.Errorf("refresh max age should be bigger than access token's one[%d - %d]", refreshMaxAge, s.MaxAge)
}
accessToken, err := s.Sign(accessClaims, accessOpts...)
if err != nil {
return TokenPair{}, err
}
refreshToken, err := Sign(s.Alg, s.Key, refreshClaims, MaxAge(refreshMaxAge))
if err != nil {
return TokenPair{}, err
}
tokenPair := jwt.NewTokenPair(accessToken, refreshToken)
return tokenPair, nil
}
View on GitHub (pinned to 7bedaf55a0)
Solutions
- Pass a refreshMaxAge clearly larger than the signer's MaxAge (e.g. access 15m, refresh 7*24h).
- Review the Signer's MaxAge configuration (WithMaxAge) and pick refreshMaxAge relative to it, not equal to it.
- If you intended refreshMaxAge as a timestamp, note it is a time.Duration from now; convert accordingly.
Example fix
// before signer.MaxAge = 15 * time.Minute pair, err := signer.NewTokenPair(access, refresh, 15*time.Minute) // error // after pair, err := signer.NewTokenPair(access, refresh, 7*24*time.Hour) // refresh outlives access
Defensive patterns
Strategy: validation
Validate before calling
if refreshMaxAge <= signer.MaxAge { return errors.New("refreshMaxAge must exceed access MaxAge") } Type guard
func validTokenPairDurations(refresh, access time.Duration) bool { return refresh > access } Try / catch
pair, err := signer.NewTokenPair(ac, rc, refreshMaxAge); if err != nil && strings.Contains(err.Error(), "refresh max age") { return fmt.Errorf("config: %w", err) } Prevention
- Define access and refresh lifetimes as named constants with refresh >> access (e.g. 15m vs 7d).
- Derive refreshMaxAge from the signer's MaxAge programmatically: s.MaxAge + margin.
- Remember refreshMaxAge is a Duration from now, not an expiry timestamp.
When it happens
Trigger: Calling signer.NewTokenPair(accessClaims, refreshClaims, refreshMaxAge) where refreshMaxAge <= s.MaxAge, e.g. both set to 15 minutes, or refreshMaxAge equal to the access lifetime.
Common situations: Misreading refreshMaxAge as an absolute timestamp instead of a duration, copying the same constant for both lifetimes, or lowering Signer MaxAge in config after hardcoding a refresh duration.
Related errors
- auth: configuration: %s access token is missing from the con
- empty regex expression
- unexpected file extension: %s
- directoryPath is empty
- path is required
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/74b88aeee6b1888d.
Report an issue: GitHub.