{"record":{"id":"dcf9cb5deb6defb7","repo":"chatboxai/chatbox","slug":"local-parser-file-too-large","errorCode":"local_parser_file_too_large","errorMessage":"local_parser_file_too_large","messagePattern":"local_parser_file_too_large","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/file-parser.ts","lineNumber":162,"sourceCode":" * which emits \"\\n\\n==== Page %d ====\\n\\n\"). Models see this exact format for PDFs\n * parsed remotely (Web/mobile) and locally (desktop); changing it on one side only\n * makes citations inconsistent across platforms.\n */\nfunction formatPdfPageMarker(pageNumber: number): string {\n  return `==== Page ${pageNumber} ====`\n}\n\n/**\n * Parse a PDF into plain text with per-page markers (see {@link formatPdfPageMarker})\n * so that models can cite PDF page numbers instead of line numbers of the extracted\n * text.\n */\nexport async function parsePdf(filePath: string): Promise<string> {\n  // Guard before reading the file into memory: pdfjs holds the buffer plus its own\n  // internal copy, so a huge PDF can transiently use several times its size.\n  const stats = await fs.stat(filePath)\n  if (stats.size > LOCAL_PARSER_MAX_PDF_FILE_SIZE) {\n    throw new Error(LOCAL_PARSER_FILE_TOO_LARGE_ERROR)\n  }\n  const { getDocument } = await loadPdfjs()\n  const fileBuffer = await fs.readFile(filePath)\n  const loadingTask = getDocument({\n    data: new Uint8Array(fileBuffer),\n    // Text extraction does not render glyphs; pdfjs package assets (cMaps,\n    // standard fonts) are not shipped with the bundled main process.\n    useSystemFonts: true,\n  })\n  try {\n    const document = await loadingTask.promise\n    const pageTexts: string[] = []\n    for (let pageNumber = 1; pageNumber <= document.numPages; pageNumber++) {\n      try {\n        const page = await document.getPage(pageNumber)\n        try {\n          const textContent = await page.getTextContent()\n          let pageText = ''","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/file-parser.ts#L144-L180","documentation":"Thrown by Claude.listModels() when the GET to `${claudeApiHost}/models?limit=990` returns a JSON body without a top-level `data` array. The whole response is JSON.stringified into the message (and ApiError prefixes it with 'API Error: '). This is a shape-guard: the Anthropic /models endpoint normally returns `{ data: [{ id, type }] }`, so a missing `data` field means the request did not reach a real Anthropic-compatible models endpoint.","triggerScenarios":"Calling listModels() with an invalid or expired `claudeApiKey`/`authToken` (Anthropic returns `{ type: 'error', error: {...} }`); a `claudeApiHost` pointing to a proxy that does not implement `/models` (returns `{}` or an OpenAI-style `{ models: [...] }`); Anthropic returns an overload/rate-limit JSON (`{ type: 'error', error: { type: 'overloaded_error' } }`); the host already includes `/v1` so the real path becomes `.../v1/v1/messages` style mismatch handled by normalizeClaudeHost incorrectly.","commonSituations":"User configures a third-party Claude-compatible proxy (e.g. one-api, openrouter-as-anthropic) whose `/models` route returns a different schema; OAuth access token expired and refresh did not run before listing models; key copied with leading/trailing whitespace; region-blocked Anthropic endpoint returning a non-`data` error body; Anthropic 529 overloaded surfaced during the model-refresh call on app start.","solutions":["Verify the API key/OAuth token is valid by sending a chat completion request — if chat works but listModels fails, the host does not implement the Anthropic /models schema.","Check `claudeApiHost`: it must point at a host whose `/models?limit=990` endpoint returns `{ data: [{ id, type: 'model' }] }`. For proxies, confirm they proxy the real Anthropic `/v1/models` route.","Inspect the JSON.stringify'd message in the thrown ApiError — it is the raw upstream body and tells you whether it is an auth error (`type:'error', error.type:'authentication_error'`), overload, or a different-schema response.","If using a proxy that only speaks OpenAI `/v1/models`, switch the provider to a Custom OpenAI provider instead of Claude, or stop calling listModels (supply model IDs manually in settings)."],"exampleFix":"// before: host returns OpenAI schema\n//   headers set, GET /models -> { models: [...] }, throws ApiError('{\"models\":[...]}')\n// after: point host at Anthropic-compatible /models, or predefine models\nconst models = settings.claudeApiHost.endsWith('anthropic.com')\n  ? await claude.listModels()\n  : [{ modelId: 'claude-sonnet-4-5', type: 'chat' }]","handlingStrategy":"try-catch","validationCode":"async function safeListClaudeModels(host: string, key: string, token?: string): Promise<boolean> {\n  const headers: Record<string,string> = { 'anthropic-version': '2023-06-01' }\n  if (token) headers['Authorization'] = `Bearer ${token}`; else headers['x-api-key'] = key\n  const res = await fetch(`${host}/v1/models?limit=990`, { headers })\n  const json = await res.json().catch(() => ({}))\n  return Array.isArray((json as any)?.data)\n}","typeGuard":"function isAnthropicModelsResponse(json: unknown): json is { data: { id: string; type: string }[] } {\n  return typeof json === 'object' && json !== null && Array.isArray((json as any).data)\n}","tryCatchPattern":"try {\n  const models = await claude.listModels()\n} catch (e) {\n  if (e instanceof ApiError) {\n    console.warn('Claude listModels upstream body:', e.message.replace('API Error: ', ''))\n    return []  // fall back to manual model list\n  }\n  throw e\n}","preventionTips":["Validate the Anthropic host with a quick /v1/models probe before storing it as the provider host.","Keep a hardcoded fallback model list so listing failures do not block chat.","Distinguish auth errors (re-prompt for key) from schema errors (suggest switching provider type)."],"tags":["api","anthropic","claude","models-list","configuration","proxy"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}