decolua/9router · error

[Usage] ${provider}: ${error.message}

Error message

[Usage] ${provider}: ${error.message}

What it means

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.

Source

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

    // Fetch usage from provider API
    let usage = await getUsageForProvider(connection, proxyOptions, { force });

    // If provider returned an auth-expired message instead of throwing,
    // force-refresh token and retry once (OAuth only)
    if (isOAuth && isAuthExpiredMessage(usage) && connection.refreshToken) {
      try {
        const retryResult = await refreshAndUpdateCredentials(connection, true, proxyOptions);
        connection = retryResult.connection;
        usage = await getUsageForProvider(connection, proxyOptions, { force });
      } catch (retryError) {
        console.warn(`[Usage] ${connection.provider}: force refresh failed: ${retryError.message}`);
      }
    }

    return Response.json(usage);
  } catch (error) {
    const provider = connection?.provider ?? "unknown";
    console.warn(`[Usage] ${provider}: ${error.message}`);
    return Response.json({ error: error.message }, { status: 500 });
  }
}

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Re-authorize the provider connection in the dashboard so fresh access/refresh tokens are stored.
  2. Check the provider name in the log message and verify the provider's usage API is reachable (network/proxy/firewall).
  3. Delete and re-add the connection if refresh tokens are revoked.
  4. Update 9Router if the provider changed its quota API endpoint or response format.

Example fix

// before: stale tokens keep failing
{ "providerConnection": { "accessToken": "expired-token", "refreshToken": "revoked" } }
// after: re-authenticated connection
{ "providerConnection": { "accessToken": "fresh-token", "refreshToken": "valid-refresh", "expiresAt": "2026-09-01T00:00:00Z" } }
Defensive patterns

Strategy: try-catch

Validate before calling

const conn = await getProviderConnectionById(id);
if (!conn) return Response.json({ error: 'Unknown connection' }, { status: 404 });
const expired = conn.expiresAt && new Date(conn.expiresAt) < new Date();
if (expired && !conn.refreshToken) return Response.json({ error: 'Re-authorize required' }, { status: 401 });

Try / catch

try {
  const usage = await getUsageForProvider(provider, credentials);
  return Response.json(usage);
} catch (error) {
  if (/expired|unauthorized|401|re-authorize/i.test(error.message)) {
    return Response.json({ error: 'auth-expired', hint: 'Re-authorize the connection' }, { status: 401 });
  }
  console.warn(`[Usage] ${provider}: ${error.message}`);
  return Response.json({ error: error.message }, { status: 502 });
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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