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
- Re-authorize the connection: delete/re-create it through the dashboard's provider connection flow so fresh accessToken/refreshToken are stored.
- 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.
- Check provider-side app authorization (revoke lists, security page) and re-consent; for GitHub Copilot also confirm device-flow token is still valid.
- 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
- Refresh tokens proactively (needsRefresh check) instead of forcing at request time
- Surface a 'Re-authorize' button whenever refresh fails and no fallback token exists
- Log provider refresh errors (HTTP status/body) to distinguish revoked vs transient failures
- Never store OAuth connections without a refreshToken when the provider supports rotation
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
- OIDC token exchange failed (${res.status})
- refresh token is required
- clientId is required for external_idp refresh
- scope is required for external_idp refresh
- ${callbackParams.error_description || callbackParams.error}
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/980b5d28bf24ad4e.
Report an issue: GitHub.