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

  1. 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
  2. Prevent double-rotation races on the client: after rotating, discard the old token and use only the returned rotated token
  3. If races are expected in your flow, serialize rotation per identity (lock or single-flight) so only one rotation proceeds
  4. Verify the token string passed in is the current one from storage, not a previously rotated copy
  5. 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

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.