{"record":{"id":"c4adb6ef3b1adbd9","repo":"decolua/9router","slug":"usage-provider-error-message","errorCode":null,"errorMessage":"[Usage] ${provider}: ${error.message}","messagePattern":"\\[Usage\\] (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"src/app/api/usage/[connectionId]/route.js","lineNumber":189,"sourceCode":"    // 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":171,"sourceCodeEnd":193,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/app/api/usage/[connectionId]/route.js#L171-L193","documentation":"The usage-quota endpoint wraps its whole GET handler (credential refresh + provider usage API call) in a try/catch. When any step fails — token refresh, upstream provider usage API, network — it logs '[Usage] <provider>: <message>' and returns a 500 JSON response carrying the raw error message to the dashboard.","triggerScenarios":"GET /api/usage/:connectionId where the connection's executor throws during refreshCredentials (e.g. 'Failed to refresh credentials. Please re-authorize the connection.'), or getUsageForProvider's upstream HTTP call to the provider's quota endpoint fails (401/403/5xx, DNS, proxy failure).","commonSituations":"Expired OAuth tokens that cannot be auto-refreshed (revoked refresh token); provider changed its usage/quota API URL or response shape; corporate proxy or DATA_DIR-less environment blocking the upstream call; deleted/invalid connection id passed in.","solutions":["Re-authorize the provider connection in the dashboard so fresh access/refresh tokens are stored.","Check the provider name in the log message and verify the provider's usage API is reachable (network/proxy/firewall).","Delete and re-add the connection if refresh tokens are revoked.","Update 9Router if the provider changed its quota API endpoint or response format."],"exampleFix":"// before: stale tokens keep failing\n{ \"providerConnection\": { \"accessToken\": \"expired-token\", \"refreshToken\": \"revoked\" } }\n// after: re-authenticated connection\n{ \"providerConnection\": { \"accessToken\": \"fresh-token\", \"refreshToken\": \"valid-refresh\", \"expiresAt\": \"2026-09-01T00:00:00Z\" } }","handlingStrategy":"try-catch","validationCode":"const conn = await getProviderConnectionById(id);\nif (!conn) return Response.json({ error: 'Unknown connection' }, { status: 404 });\nconst expired = conn.expiresAt && new Date(conn.expiresAt) < new Date();\nif (expired && !conn.refreshToken) return Response.json({ error: 'Re-authorize required' }, { status: 401 });","typeGuard":null,"tryCatchPattern":"try {\n  const usage = await getUsageForProvider(provider, credentials);\n  return Response.json(usage);\n} catch (error) {\n  if (/expired|unauthorized|401|re-authorize/i.test(error.message)) {\n    return Response.json({ error: 'auth-expired', hint: 'Re-authorize the connection' }, { status: 401 });\n  }\n  console.warn(`[Usage] ${provider}: ${error.message}`);\n  return Response.json({ error: error.message }, { status: 502 });\n}","preventionTips":["Re-authorize provider connections before tokens expire; watch the dashboard's expiry indicators.","Keep refresh tokens valid — avoid revoking app access in provider account settings.","Pin/verify proxy settings for providers that require egress through a proxy.","Update 9Router promptly when providers change their usage/quota APIs."],"tags":["oauth","token-refresh","upstream-api","http-500"],"backgroundTag":"provider-token-refresh-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}