SigNoz/signoz · error
errors.CodeUnauthenticated
errors.CodeUnauthenticated
Error message
invalid access token
What it means
RotateToken in the opaque tokenizer performs a compare-and-swap style token rotation inside a transaction; when the update fails with a NotFound error (the old token does not exist in the store), it is re-wrapped as this unauthenticated 'invalid access token' error. This correctly maps 'token not found' to a 401-style outcome for callers.
Source
Thrown at pkg/tokenizer/opaquetokenizer/provider.go:184
// If the token passed the Rotate method and is the same as the input token, return the same token.
if token.AccessToken == accessToken && token.RefreshToken == refreshToken {
rotatedToken = token
return nil
}
if err := provider.setToken(ctx, token, false); err != nil {
return err
}
// Delete the previous access token from the cache
provider.cache.Delete(ctx, emptyOrgID, accessTokenCacheKey(accessToken))
rotatedToken = token
return nil
}); err != nil {
// If the token is not found, return an unauthenticated error.
if errors.Ast(err, errors.TypeNotFound) {
return nil, errors.Wrap(err, errors.TypeUnauthenticated, errors.CodeUnauthenticated, "invalid access token")
}
return nil, err
}
return rotatedToken, nil
}
func (provider *provider) DeleteToken(ctx context.Context, accessToken string) error {
provider.cache.Delete(ctx, emptyOrgID, accessTokenCacheKey(accessToken))
if err := provider.tokenStore.DeleteByAccessToken(ctx, accessToken); err != nil {
return err
}
return nil
}
func (provider *provider) DeleteTokensByUserID(ctx context.Context, userID valuer.UUID) error {View on GitHub (pinned to 5069bf80b0)
Solutions
- Treat this error as a signal to re-authenticate: the presented token is no longer valid, so return 401 and have the client log in again
- Prevent double-rotation races on the client: after rotating, discard the old token and use only the returned rotated token
- If races are expected in your flow, serialize rotation per identity (lock or single-flight) so only one rotation proceeds
- Verify the token string passed in is the current one from storage, not a previously rotated copy
- If tokens are disappearing unexpectedly, audit logout/revocation and TTL cleanup jobs
Example fix
// before
newTok, err := provider.RotateToken(ctx, oldToken)
if err != nil { return err }
// after
newTok, err := provider.RotateToken(ctx, oldToken)
if err != nil && errors.Ast(err, errors.TypeUnauthenticated) {
return errRedirectToLogin // token already rotated/revoked; re-authenticate
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before rotating, optionally confirm the token currently exists: // (if the provider exposes a lookup) otherwise treat rotation as the check itself.
Try / catch
newTok, err := provider.RotateToken(ctx, tok)
if err != nil {
if errors.Ast(err, errors.TypeUnauthenticated) {
// old token invalid/already rotated: force re-login, do NOT retry with the same token
}
return err // non-auth failure: safe to retry
} Prevention
- On the client, replace the stored token with the rotated one atomically and never reuse the old value
- Serialize rotation per identity (single-flight/lock) to avoid concurrent double-rotation
- Treat unauthenticated rotation failures as a clean 401 path, not as a retryable error
When it happens
Trigger: Calling RotateToken with a token that was already rotated (a second concurrent rotation attempt), a revoked/deleted token, or a fabricated/garbage token string. The wrapped NotFound comes from the store layer when the row lookup/update matches nothing.
Common situations: Two racing requests rotating the same token (the first succeeds, the second's old token no longer exists), replaying an old token after refresh, tokens deleted by logout/cleanup jobs, or store backend inconsistency between the token read and the transactional update.
Related errors
AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28).
Data as JSON: /api/errors/eb6fba26db1d282f.
Report an issue: GitHub.