{"record":{"id":"5096135dc8466e46","repo":"Tencent/WeKnora","slug":"delete-invalid-mcp-oauth-client-registration-w","errorCode":null,"errorMessage":"delete invalid MCP OAuth client registration: %w","messagePattern":"delete invalid MCP OAuth client registration: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/mcp/oauth_lifecycle.go","lineNumber":247,"sourceCode":"\treturn &OAuthRefreshTemporaryError{Err: refreshErr}\n}\n\nfunc oauthTokenMaterialChanged(current, observed *types.MCPOAuthToken) bool {\n\tif current == nil || observed == nil {\n\t\treturn current != observed\n\t}\n\treturn current.AccessToken != observed.AccessToken ||\n\t\tcurrent.RefreshToken != observed.RefreshToken ||\n\t\t!current.ExpiresAt.Equal(observed.ExpiresAt)\n}\n\nfunc (r *oauthRuntime) invalidateToken(ctx context.Context, resetClient bool, reason string) error {\n\tif err := r.repo.DeleteTokenForPrincipal(ctx, r.tenantID, r.principal, r.serviceID); err != nil {\n\t\treturn fmt.Errorf(\"delete invalid MCP OAuth token: %w\", err)\n\t}\n\tif resetClient {\n\t\tif err := r.repo.DeleteClient(ctx, r.tenantID, r.serviceID); err != nil {\n\t\t\treturn fmt.Errorf(\"delete invalid MCP OAuth client registration: %w\", err)\n\t\t}\n\t}\n\treturn &OAuthReauthorizationRequiredError{Reason: reason}\n}\n\nfunc permanentRefreshFailure(err error) (permanent bool, resetClient bool) {\n\tvar oauthErr transport.OAuthError\n\tif errors.As(err, &oauthErr) {\n\t\tswitch strings.ToLower(oauthErr.ErrorCode) {\n\t\tcase \"invalid_grant\", \"invalid_token\", \"bad_refresh_token\", \"expired_token\":\n\t\t\treturn true, false\n\t\tcase \"invalid_client\", \"unauthorized_client\":\n\t\t\treturn true, true\n\t\t}\n\t}\n\tlower := strings.ToLower(err.Error())\n\tif strings.Contains(lower, \"status 400\") {\n\t\treturn true, false","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/mcp/oauth_lifecycle.go#L229-L265","documentation":"Wraps a repository error from DeleteClient when invalidating an OAuth token with resetClient=true. This happens when the client registration itself is considered bad (e.g. permanent registration failures), so both token and dynamic client registration are deleted; the function then returns OAuthReauthorizationRequiredError regardless. A failure here means the stale client registration persists.","triggerScenarios":"invalidateToken(resetClient=true) succeeds deleting the token but repo.DeleteClient fails (DB error, missing privileges, connection loss).","commonSituations":"DELETE privilege missing on the oauth client table; DB read-only replica; transient network error between the two delete calls; client row already deleted by a concurrent invalidation.","solutions":["Grant the repo DB user DELETE privileges on the client registration table","Verify the client row exists / handle already-deleted as success and retry","Check DB health (read-only mode, connectivity) indicated by the wrapped cause","Re-run authorization; dynamic registration will re-register the client if the row is gone"],"exampleFix":"// before\nif err := m.repo.DeleteClient(ctx, tenantID, serviceID); err != nil {\n    return err\n}\n// after: tolerate not-exists, surface real failures\nif err := m.repo.DeleteClient(ctx, tenantID, serviceID); err != nil && !errors.Is(err, sql.ErrNoRows) {\n    return fmt.Errorf(\"delete invalid MCP OAuth client registration: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// verify client table is writable before starting OAuth flows\nif err := db.PingContext(ctx); err != nil { return err }","typeGuard":null,"tryCatchPattern":"err := mgr.StartAuthorizationForService(ctx, svc, tenantID, principal, redirect, \"\")\nif err != nil && strings.Contains(err.Error(), \"delete invalid MCP OAuth client registration\") {\n    // stale registration persists; fall back to manual cleanup then retry once\n    _ = repo.DeleteClient(ctx, tenantID, svc.ID)\n    err = mgr.StartAuthorizationForService(ctx, svc, tenantID, principal, redirect, \"\")\n}\nif err != nil { return err }","preventionTips":["Grant DELETE privileges on the oauth client table","Treat 'row not found' on delete as success to avoid spurious failures","Keep token+client deletion in a transaction when possible","Alert on invalidation failures; they block user reauthorization"],"tags":["oauth","database","persistence","client-registration"],"backgroundTag":"oauth-client-registration-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}