{"record":{"id":"a79545b21c0bbbe8","repo":"QuantumNous/new-api","slug":"failed-to-fetch-usage","errorCode":null,"errorMessage":"Failed to fetch usage","messagePattern":"Failed to fetch usage","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"web/src/features/channels/components/channels-columns.tsx","lineNumber":431,"sourceCode":"        </Tooltip>\n      </TooltipProvider>\n    )\n  }\n\n  // Regular channel row: show used and remaining with click to update\n  const variant = getBalanceVariant(balance)\n\n  const handleClickUpdate = async () => {\n    if (isUpdating) {\n      return\n    }\n\n    setIsUpdating(true)\n    if (channel.type === 57) {\n      try {\n        const res = await getCodexUsage(channel.id)\n        if (!res.success) {\n          throw new Error(res.message || t('Failed to fetch usage'))\n        }\n        setCodexUsageResponse(res)\n        setCodexUsageOpen(true)\n      } catch (error) {\n        toast.error(\n          error instanceof Error ? error.message : t('Failed to fetch usage')\n        )\n      } finally {\n        setIsUpdating(false)\n      }\n      return\n    }\n\n    await handleUpdateChannelBalance(channel.id, queryClient)\n    setIsUpdating(false)\n  }\n  let remainingBadgeLabel = sensitiveVisible ? remainingDisplay : SENSITIVE_MASK\n  if (sensitiveVisible && isUpdating) {","sourceCodeStart":413,"sourceCodeEnd":449,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/channels-columns.tsx#L413-L449","documentation":"Thrown in the channels table's update-balance/usage action for channel type 57 (Codex) when getCodexUsage(channel.id) returns success:false, or as the catch-path fallback when a non-Error exception occurs. It aborts opening the Codex usage dialog and surfaces a toast.","triggerScenarios":"Clicking the usage/update action on a channel row with channel.type === 57 invokes GET of the Codex usage endpoint; the backend returns success:false (channel credentials rejected upstream, channel disabled, or internal error).","commonSituations":"Codex channel's API key invalid or expired so usage lookup fails upstream; channel not yet tested/saved with valid credentials; upstream Codex API outage or rate limit; channel deleted by another admin between render and click.","solutions":["Read the toast — res.message from the backend states the upstream reason (auth failure, quota endpoint error).","Open the channel edit dialog and verify/re-enter the Codex credentials, then retest the channel.","Confirm the channel is enabled and of the correct type/key format expected by the Codex usage endpoint.","If upstream is down, retry later; usage fetching depends on the provider's availability."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"if (channel.type !== 57 || isUpdating) return // only Codex channels hit this path","typeGuard":"function isCodexUsageSuccess(res: unknown): res is { success: true; data: unknown } {\n  return typeof res === 'object' && res !== null && (res as any).success === true\n}","tryCatchPattern":"try {\n  const res = await getCodexUsage(channel.id)\n  if (!res.success) throw new Error(res.message || t('Failed to fetch usage'))\n  setCodexUsageResponse(res)\n  setCodexUsageOpen(true)\n} catch (error) {\n  toast.error(error instanceof Error ? error.message : t('Failed to fetch usage'))\n} finally {\n  setIsUpdating(false)\n}","preventionTips":["Keep Codex channel credentials valid and the channel enabled before querying usage","Rate-limit the usage button (isUpdating guard) and surface res.message verbatim"],"tags":["channels","codex","usage","upstream"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}