{"record":{"id":"d738fcdfb4b1304e","repo":"QuantumNous/new-api","slug":"failed-to-fetch-reset-credit-details","errorCode":null,"errorMessage":"Failed to fetch reset credit details","messagePattern":"Failed to fetch reset credit details","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/features/channels/components/dialogs/codex-usage-dialog.tsx","lineNumber":986,"sourceCode":"      ? response?.message?.trim() || t('Failed to fetch usage')\n      : ''\n\n  const loadResetCredits = useCallback(\n    async (force = false) => {\n      if (!channelId) {\n        setResetCreditsError(t('Channel ID is required'))\n        return\n      }\n      if (isLoadingResetCredits || (!force && resetCreditsResponse)) {\n        return\n      }\n\n      setIsLoadingResetCredits(true)\n      setResetCreditsError('')\n      try {\n        const res = await getCodexResetCredits(channelId)\n        if (!res.success) {\n          throw new Error(\n            res.message || t('Failed to fetch reset credit details')\n          )\n        }\n        setResetCreditsResponse(res)\n      } catch (error) {\n        setResetCreditsError(\n          error instanceof Error\n            ? error.message\n            : t('Failed to fetch reset credit details')\n        )\n      } finally {\n        setIsLoadingResetCredits(false)\n      }\n    },\n    [channelId, isLoadingResetCredits, resetCreditsResponse, t]\n  )\n\n  const handleResetCreditsOpenChange = (nextOpen: boolean) => {","sourceCodeStart":968,"sourceCodeEnd":1004,"githubUrl":"https://github.com/QuantumNous/new-api/blob/e2c7aa7b102c2075eae2377df3508658d45e88dc/web/src/features/channels/components/dialogs/codex-usage-dialog.tsx#L968-L1004","documentation":"Set as resetCreditsError inside codex-usage-dialog when loading reset-credit details fails. The effect first guards on channelId being present and on isLoadingResetCredits/force, then calls getCodexResetCredits(channelId); a success=false response or thrown request stores the error message in component state instead of a toast.","triggerScenarios":"Opening the reset-credits section of the Codex usage dialog for a channel whose id is set, where getCodexResetCredits resolves success=false (upstream error, invalid credential) or the request rejects (network/session). Also triggered with force=true when re-fetching after a previous failure.","commonSituations":"Codex credential lacks permission to read reset-credit windows; upstream API changed shape; admin token expired while the dialog was open; backend proxy cannot reach the Codex billing endpoint.","solutions":["Read the exact resetCreditsError text rendered in the dialog — when res.message exists it carries the backend reason","Re-save the channel's Codex credential and reopen the dialog to re-trigger the fetch","Verify the channel id still resolves (channel not deleted) and your session is valid","Check backend logs for the upstream reset-credits call (status code and body)"],"exampleFix":"// before\nconst res = await getCodexResetCredits(channelId)\nif (!res.success) {\n  throw new Error(res.message || t('Failed to fetch reset credit details'))\n}\n// after - keep last good data on refresh failure\nconst res = await getCodexResetCredits(channelId)\nif (!res.success) {\n  throw new Error(res.message || t('Failed to fetch reset credit details'))\n}\nsetResetCreditsResponse((prev) => (force ? res : res ?? prev))","handlingStrategy":"try-catch","validationCode":"if (!channelId) { setResetCreditsError(t('Channel ID is required')); return }\nif (isLoadingResetCredits || (!force && resetCreditsResponse)) return","typeGuard":"const hasChannelId = (id: string | number | null | undefined): boolean =>\n  id !== null && id !== undefined && id !== ''","tryCatchPattern":"setIsLoadingResetCredits(true)\nsetResetCreditsError('')\ntry {\n  const res = await getCodexResetCredits(channelId)\n  if (!res.success) throw new Error(res.message || t('Failed to fetch reset credit details'))\n  setResetCreditsResponse(res)\n} catch (error) {\n  setResetCreditsError(error instanceof Error ? error.message : t('Failed to fetch reset credit details'))\n} finally {\n  setIsLoadingResetCredits(false)\n}","preventionTips":["Keep the channelId/loading/force guards so the effect is idempotent","Store errors in state (not just toast) so they persist beside the data section","Offer a retry button that calls with force=true after failure"],"tags":["api-error","codex","reset-credits","dialog","state-error"],"backgroundTag":null,"analyzedSha":"e2c7aa7b102c2075eae2377df3508658d45e88dc","analyzedAt":"2026-08-15T10:35:18.111Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}