Tencent/WeKnora · error

reload MCP OAuth token after concurrent refresh: %w

Error message

reload MCP OAuth token after concurrent refresh: %w

What it means

Wraps a repository error that occurred while re-reading the stored OAuth token after waiting on the refresh lease poll. After another principal's concurrent refresh completes, the runtime reloads the token to pick up the newly rotated credentials; if that reload fails, the underlying storage error is wrapped and returned to ensureFresh. It signals a persistence-layer problem during the post-refresh reconciliation, not an OAuth protocol failure.

Source

Thrown at internal/mcp/oauth_lifecycle.go:169

		leaseUntil := time.Now().Add(leaseDuration)
		acquired, err := r.repo.TryAcquireTokenRefreshLease(
			ctx, r.tenantID, r.principal, r.serviceID, leaseID, leaseUntil,
		)
		if err != nil {
			return fmt.Errorf("claim MCP OAuth token refresh: %w", err)
		}
		if acquired {
			return r.refreshAsLeaseOwner(ctx, observed, leaseID, override)
		}

		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(oauthRefreshPoll):
		}
		current, err := r.repo.GetTokenForPrincipal(ctx, r.tenantID, r.principal, r.serviceID)
		if err != nil {
			return fmt.Errorf("reload MCP OAuth token after concurrent refresh: %w", err)
		}
		if current == nil || current.AccessToken == "" {
			return &OAuthReauthorizationRequiredError{Reason: "the refresh token is no longer valid"}
		}
		if oauthTokenMaterialChanged(current, observed) {
			if current.ExpiresAt.IsZero() || current.ExpiresAt.After(time.Now().Add(oauthRefreshSkew)) {
				return nil
			}
			observed = current
		}
	}
}

func (r *oauthRuntime) refreshAsLeaseOwner(
	ctx context.Context, observed *types.MCPOAuthToken, leaseID string, override *transport.OAuthHandler,
) error {
	defer func() {
		releaseCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second)

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Inspect the wrapped cause (%w) for a DB connectivity error and restore the database connection/pool
  2. Retry the token refresh operation once connectivity is restored; the lease mechanism is safe to re-enter
  3. Increase request/context timeouts if cancellation during oauthRefreshPoll is the cause
  4. Verify the token repository table/schema exists for this tenant and service

Example fix

// before
lexer := oauth.NewLifecycle(repo)
tok, err := lexer.EnsureFresh(ctx, svc, principal)
// after: retry with backoff on transient repo failures
var tok *types.Token
err := retry.Do(func() error {
    var e error
    tok, e = lexer.EnsureFresh(ctx, svc, principal)
    return e
}, retry.OnRetry(func(n uint, err error) {
    log.Warnf("retrying oauth refresh after transient error: %v", err)
}))
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("token store unavailable before refresh: %w", err)
}

Try / catch

tok, err := rt.EnsureFresh(ctx, tenantID, principal, serviceID)
if err != nil && strings.Contains(err.Error(), "reload MCP OAuth token after concurrent refresh") {
    // transient repo failure: retry with backoff
    return backoff.Retry(func() error { _, err = rt.EnsureFresh(ctx, tenantID, principal, serviceID); return err })
}
if err != nil {
    var reauth *mcp.OAuthReauthorizationRequiredError
    if errors.As(err, &reauth) { return startReauth(ctx, reauth) }
    return err
}

Prevention

When it happens

Trigger: refreshWithLease loses (or waits out) the lease race, then calls repo.GetTokenForPrincipal and the repo returns an error (DB down, connection pool exhausted, context deadline, table missing).

Common situations: Database outage or transient network blip mid-refresh; Postgres/MySQL connection limit reached under load; the request context is cancelled while polling oauthRefreshPoll; schema migration left the token table unavailable.

Related errors


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