decolua/9router · error

Failed to refresh credentials. Please re-authorize the conne

Error message

Failed to refresh credentials. Please re-authorize the connection.

What it means

refreshAndUpdateCredentials refreshes an OAuth connection's tokens via the provider executor's refreshCredentials. If the refresh returns falsy (failed) AND the connection has no accessToken to fall back on, it throws this error telling the user the connection must be re-authorized. Note: if refresh fails but an accessToken exists, it degrades gracefully and returns refreshed:false instead of throwing.

Source

Thrown at src/app/api/usage/[connectionId]/route.js:55

    copilotTokenExpiresAt: connection.providerSpecificData?.copilotTokenExpiresAt,
  };

  // Check if refresh is needed (skip when force=true)
  const needsRefresh = force || executor.needsRefresh(credentials);

  if (!needsRefresh) {
    return { connection, refreshed: false };
  }

  // Use executor's refreshCredentials method (with optional proxy)
  const refreshResult = await executor.refreshCredentials(credentials, console, proxyOptions);

  if (!refreshResult) {
    // Refresh failed but we still have an accessToken — try with existing token
    if (connection.accessToken) {
      return { connection, refreshed: false };
    }
    throw new Error("Failed to refresh credentials. Please re-authorize the connection.");
  }

  // Build update object
  const now = new Date().toISOString();
  const updateData = {
    updatedAt: now,
  };

  // Update accessToken if present
  if (refreshResult.accessToken) {
    updateData.accessToken = refreshResult.accessToken;
  }

  // Update refreshToken if present
  if (refreshResult.refreshToken) {
    updateData.refreshToken = refreshResult.refreshToken;
  }

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Re-authorize the connection: delete/re-create it through the dashboard's provider connection flow so fresh accessToken/refreshToken are stored.
  2. Verify the connection actually has a refreshToken stored; if it was saved without one (e.g. API-key or PKCE-only flow), refresh can never succeed — re-auth is mandatory.
  3. Check provider-side app authorization (revoke lists, security page) and re-consent; for GitHub Copilot also confirm device-flow token is still valid.
  4. If refreshes fail only behind a proxy, verify resolveConnectionProxyConfig output — a broken proxy can make the token endpoint unreachable, causing refreshResult to be falsy.

Example fix

// before
if (!refreshResult) {
  if (connection.accessToken) return { connection, refreshed: false };
  throw new Error("Failed to refresh credentials. Please re-authorize the connection.");
}
// after
if (!refreshResult) {
  if (connection.accessToken) return { connection, refreshed: false };
  const err = new Error("Failed to refresh credentials. Please re-authorize the connection.");
  err.code = "OAUTH_REFRESH_FAILED";
  err.connectionId = connection.id;
  err.provider = connection.provider;
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before calling the usage route, check the connection can possibly refresh
function canRefreshOrUse(connection) {
  return Boolean(connection.accessToken || connection.refreshToken);
}
if (!canRefreshOrUse(connection)) throw new Error("Connection has no tokens — re-authorize first");

Type guard

function hasUsableCredentials(conn) {
  return typeof conn?.accessToken === "string" && conn.accessToken.length > 0;
}

Try / catch

try {
  const { connection, refreshed } = await refreshAndUpdateCredentials(conn, true);
} catch (err) {
  if (err.message.includes("re-authorize")) {
    // Mark connection as needs-reauth in UI and start the provider OAuth flow again
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: GET (or POST) /api/usage/<connectionId> runs on a connection whose executor.needsRefresh() says a refresh is due (or force=true), the executor's refreshCredentials fails (invalid/expired refresh_token, revoked grant, provider OAuth endpoint error, network/proxy failure) and connection.accessToken is empty/undefined.

Common situations: Provider revoked or rotated the refresh token (Google/Anthropic/OpenAI revoke stale or unused refresh tokens), user revoked app access in provider account settings, refresh token expired after provider-imposed lifetime (e.g. 7-180 days), or the stored connection was created without a refreshToken/accessToken (API-key-only credentials passed to an OAuth executor).

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/980b5d28bf24ad4e. Report an issue: GitHub.