{"record":{"id":"21f4007439b6522f","repo":"bytedance/deer-flow","slug":"failed-to-load-thread-token-usage","errorCode":null,"errorMessage":"Failed to load thread token usage.","messagePattern":"Failed to load thread token usage\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"frontend/src/core/threads/api.ts","lineNumber":79,"sourceCode":"  }\n  return fallback;\n}\n\nexport async function fetchThreadTokenUsage(\n  threadId: string,\n): Promise<ThreadTokenUsageResponse | null> {\n  const response = await fetchWithAuth(\n    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/token-usage`,\n    {\n      method: \"GET\",\n    },\n  );\n\n  if (!response.ok) {\n    if (response.status === 403 || response.status === 404) {\n      return null;\n    }\n    throw new Error(\"Failed to load thread token usage.\");\n  }\n\n  return (await response.json()) as ThreadTokenUsageResponse;\n}\n\nexport async function branchThreadFromTurn(\n  threadId: string,\n  input: BranchThreadFromTurnInput,\n): Promise<ThreadBranchResponse> {\n  const response = await fetchWithAuth(\n    `${getBackendBaseURL()}/api/threads/${encodeURIComponent(threadId)}/branches`,\n    {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({\n        message_id: input.messageId,","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/frontend/src/core/threads/api.ts#L61-L97","documentation":"Thrown when GET /api/threads/{id}/token-usage fails with a status other than 403/404 (those deliberately return null because older or restricted backends don't expose usage). The endpoint aggregates model token usage per thread; unexpected failures are 401 (expired session via fetchWithAuth) or 5xx.","triggerScenarios":"Loading token usage after session expiry (401); usage aggregation hitting a persistence error (500); very long threads timing out the aggregation query (504).","commonSituations":"Token/stats panel on legacy threads; backend upgraded with new usage schema but migrations pending; slow storage under load.","solutions":["Wrap the call and hide the usage widget on failure — usage is auxiliary data","On 401, re-authenticate and retry once","Apply pending migrations so usage tables match the backend version","For 504 on huge threads, open usage on demand rather than on thread load"],"exampleFix":"// before\nconst usage = await fetchThreadTokenUsage(threadId);\n\n// after\nconst usage = await fetchThreadTokenUsage(threadId).catch(() => null);\nif (usage === null) hideUsageWidget();","handlingStrategy":"fallback","validationCode":null,"typeGuard":"export function isThreadTokenUsageError(e: unknown): e is Error {\n  return e instanceof Error && e.message === 'Failed to load thread token usage.';\n}","tryCatchPattern":"try {\n  return await fetchThreadTokenUsage(threadId);\n} catch (e) {\n  if (isThreadTokenUsageError(e)) return null; // usage is auxiliary\n  throw e;\n}","preventionTips":["Load usage lazily, not on every thread open","Re-auth on 401 via fetchWithAuth interceptor instead of erroring here","Hide the widget on null rather than showing an error"],"tags":["threads","token-usage","http","frontend"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}