Tencent/WeKnora · error

load MCP OAuth token: %w

Error message

load MCP OAuth token: %w

What it means

oauthRuntime.ensureFresh loads the stored OAuth token row via repo.GetTokenForPrincipal; any repository failure is wrapped as "load MCP OAuth token: %w". A nil row or empty AccessToken is handled separately as OAuthReauthorizationRequiredError, so this error specifically means the token lookup itself failed (DB error, context cancelled, etc.).

Source

Thrown at internal/mcp/oauth_lifecycle.go:119

	if !token.ExpiresAt.IsZero() {
		expiresAt := token.ExpiresAt
		status.ExpiresAt = &expiresAt
	}
	if token.ExpiresAt.IsZero() || token.ExpiresAt.After(now) {
		status.Authorized = true
		status.State = oauthStateAuthorized
		return status
	}
	if status.RefreshAvailable {
		status.State = oauthStateRefreshable
	}
	return status
}

func (r *oauthRuntime) ensureFresh(ctx context.Context, force bool, override *transport.OAuthHandler) error {
	row, err := r.repo.GetTokenForPrincipal(ctx, r.tenantID, r.principal, r.serviceID)
	if err != nil {
		return fmt.Errorf("load MCP OAuth token: %w", err)
	}
	if row == nil || row.AccessToken == "" {
		return &OAuthReauthorizationRequiredError{Reason: "no token is stored"}
	}
	now := time.Now()
	if !force {
		if row.ExpiresAt.IsZero() || row.ExpiresAt.After(now.Add(oauthRefreshSkew)) {
			return nil
		}
		// Tokens issued without refresh_token remain usable through their actual
		// expiry; the refresh skew must not shorten their lifetime.
		if row.RefreshToken == "" && row.ExpiresAt.After(now) {
			return nil
		}
	}
	if row.RefreshToken == "" {
		_ = r.repo.DeleteTokenForPrincipal(ctx, r.tenantID, r.principal, r.serviceID)
		return &OAuthReauthorizationRequiredError{Reason: "the access token expired and no refresh token is available"}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Inspect the wrapped cause for the DB-level error (connection refused, unknown table, cancelled context)
  2. Verify the OAuth token storage table exists and migrations are applied
  3. Check database connectivity and pool health
  4. If the lookup succeeds but no token exists, expect OAuthReauthorizationRequiredError instead — trigger the re-auth flow

Example fix

// before
row, err := repo.GetTokenForPrincipal(ctx, tenantID, principal, serviceID)
if err != nil { return err } // opaque DB failure
// after
if err != nil {
    logger.Error("token lookup failed", "err", err)
    return fmt.Errorf("load MCP OAuth token: %w", err) // already wrapped by ensureFresh; handle upstream
}
Defensive patterns

Strategy: try-catch

Validate before calling

row, err := repo.GetTokenForPrincipal(ctx, tenantID, principal, serviceID)
if err != nil { return fmt.Errorf("token store unavailable: %w", err) }
if row == nil || row.AccessToken == "" { return errors.New("no stored token; run OAuth flow first") }

Type guard

func isTokenLoadFailure(err error) bool { return err != nil && strings.Contains(err.Error(), "load MCP OAuth token") }

Try / catch

err := runtime.EnsureFresh(ctx, false, nil)
if err != nil {
    var reauth *mcp.OAuthReauthorizationRequiredError
    if errors.As(err, &reauth) { /* re-auth */ }
    if isTokenLoadFailure(err) { /* check DB health / migrations */ }
    return err
}

Prevention

When it happens

Trigger: ensureFresh (invoked via oauthCall during any MCP call, or directly in tests) when GetTokenForPrincipal returns a database error, the context is cancelled mid-query, or the repo layer is unavailable.

Common situations: Database connection pool exhausted or down; migration missing so the token table doesn't exist; context cancelled because an upstream request timed out; wrong tenant/principal IDs causing a query error.

Related errors


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