{"record":{"id":"d9a5de3f4cfd1d7d","repo":"decolua/9router","slug":"usage-connection-provider-force-refresh-fail","errorCode":null,"errorMessage":"[Usage] ${connection.provider}: force refresh failed: ${retryError.message}","messagePattern":"\\[Usage\\] (.+?): force refresh failed: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/app/api/usage/[connectionId]/route.js","lineNumber":182,"sourceCode":"        console.error(\"[Usage API] Credential refresh failed:\", refreshError);\n        return Response.json({\n          error: `Credential refresh failed: ${refreshError.message}`\n        }, { status: 401 });\n      }\n    }\n\n    // Fetch usage from provider API\n    let usage = await getUsageForProvider(connection, proxyOptions, { force });\n\n    // If provider returned an auth-expired message instead of throwing,\n    // force-refresh token and retry once (OAuth only)\n    if (isOAuth && isAuthExpiredMessage(usage) && connection.refreshToken) {\n      try {\n        const retryResult = await refreshAndUpdateCredentials(connection, true, proxyOptions);\n        connection = retryResult.connection;\n        usage = await getUsageForProvider(connection, proxyOptions, { force });\n      } catch (retryError) {\n        console.warn(`[Usage] ${connection.provider}: force refresh failed: ${retryError.message}`);\n      }\n    }\n\n    return Response.json(usage);\n  } catch (error) {\n    const provider = connection?.provider ?? \"unknown\";\n    console.warn(`[Usage] ${provider}: ${error.message}`);\n    return Response.json({ error: error.message }, { status: 500 });\n  }\n}\n","sourceCodeStart":164,"sourceCodeEnd":193,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/app/api/usage/[connectionId]/route.js#L164-L193","documentation":"Console.warn in the GET usage handler when the force-refresh retry path fails. If the provider usage call reports an expired OAuth token (isAuthExpiredMessage) and a refresh token exists, the route refreshes credentials and re-fetches usage; if the refresh or the retry usage fetch throws, this message is logged and the original stale `usage` object is returned in the JSON response.","triggerScenarios":"GET /api/usage/[connectionId] on an OAuth provider where the usage response indicates an expired token, and refreshAndUpdateCredentials throws (revoked refresh token, token endpoint unreachable, bad proxy) or getUsageForProvider throws on the retry (network error).","commonSituations":"Accounts idle past the refresh token lifetime; upstream provider OAuth endpoint outage; proxy misconfiguration; expired/revoked refresh tokens after password change or app deauthorization at the provider.","solutions":["Re-authenticate the OAuth connection in the dashboard to obtain a fresh refresh token.","Check the underlying retryError.message (e.g. invalid_grant vs ENOTFOUND) to distinguish credential death from network issues.","Verify proxy settings and outbound connectivity to the provider's token/usage endpoints.","Retry the GET; if it persists, disable/re-add the connection."],"exampleFix":"// before\ncatch (retryError) {\n  console.warn(`[Usage] ${connection.provider}: force refresh failed: ${retryError.message}`);\n}\n// after\ncatch (retryError) {\n  console.warn(`[Usage] ${connection.provider}: force refresh failed: ${retryError.message}`);\n  usage = { ...usage, authError: retryError.message, needsReauth: retryError.message.includes(\"invalid_grant\") };\n}","handlingStrategy":"try-catch","validationCode":"// Check connection health before querying usage\nif (!conn.refreshToken) console.warn(\"No refresh token — usage may fail on expired access token\");","typeGuard":"function isUsagePayload(u) { return u && typeof u === \"object\" && !u.error; }","tryCatchPattern":"try {\n  const res = await fetch(`/api/usage/${connId}`);\n  const usage = await res.json();\n  if (usage?.needsReauth || usage?.authError) await reauthenticate(connId);\n} catch (e) { /* handle fetch failure */ }","preventionTips":["Periodically refresh OAuth connections proactively instead of waiting for the expired-token path.","Watch for repeated force-refresh warnings per provider; treat them as signals to re-add the account.","Verify proxy/firewall rules allow the host to reach each provider's token endpoint.","Alert on usage responses that consistently come back stale."],"tags":["oauth","token-refresh","network","fallback"],"backgroundTag":"oauth-refresh-token-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}