SigNoz/signoz · error · errors SigNozError
CodeUnauthenticated
CodeUnauthenticated
Error message
invalid token pair
What it means
Returned by Token.Rotate when the caller presents the previous token pair but the token has already been rotated within the rotation window, so replaying the old pair is rejected. Protects against reuse of a consumed pair.
Source
Thrown at pkg/types/authtypes/token.go:171
if !typ.RotatedAt.IsZero() && typ.RotatedAt.Before(time.Now().Add(-rotationInterval)) {
return errors.New(errors.TypeUnauthenticated, ErrCodeTokenRotationRequired, "token needs to be rotated")
}
if typ.RotatedAt.IsZero() && typ.CreatedAt.Before(time.Now().Add(-rotationInterval)) {
return errors.New(errors.TypeUnauthenticated, ErrCodeTokenRotationRequired, "token needs to be rotated")
}
return nil
}
func (typ *Token) Rotate(accessTokenOrPrevAccessToken string, refreshTokenOrPrevRefreshToken string, rotationDuration time.Duration, idleDuration time.Duration, maxDuration time.Duration) error {
if typ.PrevAccessToken == accessTokenOrPrevAccessToken && typ.PrevRefreshToken == refreshTokenOrPrevRefreshToken {
// If the token has been rotated within the rotation duration, do nothing and return the same token.
if !typ.RotatedAt.IsZero() && typ.RotatedAt.Before(time.Now().Add(-rotationDuration)) {
return nil
}
return errors.New(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "invalid token pair")
}
if typ.AccessToken != accessTokenOrPrevAccessToken || typ.RefreshToken != refreshTokenOrPrevRefreshToken {
return errors.New(errors.TypeUnauthenticated, errors.CodeUnauthenticated, "invalid token pair")
}
if err := typ.IsExpired(idleDuration, maxDuration); err != nil {
return err
}
// Generate new access and refresh tokens.
typ.PrevAccessToken = typ.AccessToken
typ.AccessToken = password.MustGenerate(32, 10, 0, true, true)
typ.PrevRefreshToken = typ.RefreshToken
typ.RefreshToken = password.MustGenerate(32, 12, 0, true, true)
// Set the rotated at time.
typ.RotatedAt = time.Now()View on GitHub (pinned to 5069bf80b0)
Solutions
- Persist the new token pair immediately after a successful rotate, before any retry
- On this error, re-fetch the current token (e.g. from your store) and use it
- Make rotate calls idempotent-safe by serializing them (lock) per token
Defensive patterns
Strategy: retry
Try / catch
err := tok.Rotate(at, rt, rot, idle, max)
if err != nil && strings.Contains(err.Error(), "invalid token pair") {
// reload latest pair from store and retry once
} Prevention
- Persist the new pair atomically right after rotation
- Serialize rotate calls per token with a lock
- Retry with backoff only after refreshing the stored pair
When it happens
Trigger: Calling Rotate twice with the same old (prev) token pair within rotationDuration; or a retry of a partially-failed rotate request.
Common situations: Network retry after a successful-but-unacknowledged rotation, race between two processes rotating the same token, or a client not persisting the newly rotated pair.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ErrCodeTokenRotationRequired
- CodeInvalidInput
- ErrCodeTokenExpired
- ErrCodeTokenOlderLastObservedAt
- ErrCodeResetPasswordTokenExpired
AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28).
Data as JSON: /api/errors/1e860a2fe2e77cde.
Report an issue: GitHub.