{"record":{"id":"eb6fba26db1d282f","repo":"SigNoz/signoz","slug":"errors-codeunauthenticated-eb6fba","errorCode":"errors.CodeUnauthenticated","errorMessage":"invalid access token","messagePattern":"invalid access token","errorType":"error_code","errorClass":null,"httpStatus":401,"severity":"error","filePath":"pkg/tokenizer/opaquetokenizer/provider.go","lineNumber":184,"sourceCode":"\t\t// If the token passed the Rotate method and is the same as the input token, return the same token.\n\t\tif token.AccessToken == accessToken && token.RefreshToken == refreshToken {\n\t\t\trotatedToken = token\n\t\t\treturn nil\n\t\t}\n\n\t\tif err := provider.setToken(ctx, token, false); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// Delete the previous access token from the cache\n\t\tprovider.cache.Delete(ctx, emptyOrgID, accessTokenCacheKey(accessToken))\n\n\t\trotatedToken = token\n\t\treturn nil\n\t}); err != nil {\n\t\t// If the token is not found, return an unauthenticated error.\n\t\tif errors.Ast(err, errors.TypeNotFound) {\n\t\t\treturn nil, errors.Wrap(err, errors.TypeUnauthenticated, errors.CodeUnauthenticated, \"invalid access token\")\n\t\t}\n\n\t\treturn nil, err\n\t}\n\n\treturn rotatedToken, nil\n}\n\nfunc (provider *provider) DeleteToken(ctx context.Context, accessToken string) error {\n\tprovider.cache.Delete(ctx, emptyOrgID, accessTokenCacheKey(accessToken))\n\tif err := provider.tokenStore.DeleteByAccessToken(ctx, accessToken); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (provider *provider) DeleteTokensByUserID(ctx context.Context, userID valuer.UUID) error {","sourceCodeStart":166,"sourceCodeEnd":202,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/pkg/tokenizer/opaquetokenizer/provider.go#L166-L202","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nnewTok, err := provider.RotateToken(ctx, oldToken)\nif err != nil { return err }\n\n// after\nnewTok, err := provider.RotateToken(ctx, oldToken)\nif err != nil && errors.Ast(err, errors.TypeUnauthenticated) {\n    return errRedirectToLogin // token already rotated/revoked; re-authenticate\n}","handlingStrategy":"try-catch","validationCode":"// Before rotating, optionally confirm the token currently exists:\n// (if the provider exposes a lookup) otherwise treat rotation as the check itself.","typeGuard":null,"tryCatchPattern":"newTok, err := provider.RotateToken(ctx, tok)\nif err != nil {\n    if errors.Ast(err, errors.TypeUnauthenticated) {\n        // old token invalid/already rotated: force re-login, do NOT retry with the same token\n    }\n    return err // non-auth failure: safe to retry\n}","preventionTips":["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"],"tags":["opaque-token","token-rotation","not-found","authentication","race-condition","go"],"backgroundTag":"token-rotation-race-invalid-token","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}