Tencent/WeKnora · error

delete invalid MCP OAuth client registration: %w

Error message

delete invalid MCP OAuth client registration: %w

What it means

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.

Source

Thrown at internal/mcp/oauth_lifecycle.go:247

	return &OAuthRefreshTemporaryError{Err: refreshErr}
}

func oauthTokenMaterialChanged(current, observed *types.MCPOAuthToken) bool {
	if current == nil || observed == nil {
		return current != observed
	}
	return current.AccessToken != observed.AccessToken ||
		current.RefreshToken != observed.RefreshToken ||
		!current.ExpiresAt.Equal(observed.ExpiresAt)
}

func (r *oauthRuntime) invalidateToken(ctx context.Context, resetClient bool, reason string) error {
	if err := r.repo.DeleteTokenForPrincipal(ctx, r.tenantID, r.principal, r.serviceID); err != nil {
		return fmt.Errorf("delete invalid MCP OAuth token: %w", err)
	}
	if resetClient {
		if err := r.repo.DeleteClient(ctx, r.tenantID, r.serviceID); err != nil {
			return fmt.Errorf("delete invalid MCP OAuth client registration: %w", err)
		}
	}
	return &OAuthReauthorizationRequiredError{Reason: reason}
}

func permanentRefreshFailure(err error) (permanent bool, resetClient bool) {
	var oauthErr transport.OAuthError
	if errors.As(err, &oauthErr) {
		switch strings.ToLower(oauthErr.ErrorCode) {
		case "invalid_grant", "invalid_token", "bad_refresh_token", "expired_token":
			return true, false
		case "invalid_client", "unauthorized_client":
			return true, true
		}
	}
	lower := strings.ToLower(err.Error())
	if strings.Contains(lower, "status 400") {
		return true, false

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Grant the repo DB user DELETE privileges on the client registration table
  2. Verify the client row exists / handle already-deleted as success and retry
  3. Check DB health (read-only mode, connectivity) indicated by the wrapped cause
  4. Re-run authorization; dynamic registration will re-register the client if the row is gone

Example fix

// before
if err := m.repo.DeleteClient(ctx, tenantID, serviceID); err != nil {
    return err
}
// after: tolerate not-exists, surface real failures
if err := m.repo.DeleteClient(ctx, tenantID, serviceID); err != nil && !errors.Is(err, sql.ErrNoRows) {
    return fmt.Errorf("delete invalid MCP OAuth client registration: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify client table is writable before starting OAuth flows
if err := db.PingContext(ctx); err != nil { return err }

Try / catch

err := mgr.StartAuthorizationForService(ctx, svc, tenantID, principal, redirect, "")
if err != nil && strings.Contains(err.Error(), "delete invalid MCP OAuth client registration") {
    // stale registration persists; fall back to manual cleanup then retry once
    _ = repo.DeleteClient(ctx, tenantID, svc.ID)
    err = mgr.StartAuthorizationForService(ctx, svc, tenantID, principal, redirect, "")
}
if err != nil { return err }

Prevention

When it happens

Trigger: invalidateToken(resetClient=true) succeeds deleting the token but repo.DeleteClient fails (DB error, missing privileges, connection loss).

Common situations: 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.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/5096135dc8466e46. Report an issue: GitHub.