{"record":{"id":"2ef6fc01eeebc7b4","repo":"chatboxai/chatbox","slug":"no-content-extracted-from-file","errorCode":null,"errorMessage":"No content extracted from file","messagePattern":"No content extracted from file","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/knowledge-base/file-loaders.ts","lineNumber":130,"sourceCode":"    })\n\n    // 2. Chunking\n    const allChunks = await doc.chunk({\n      strategy: 'recursive',\n      maxSize: 1200,\n      overlap: 150,\n    })\n\n    if (!allChunks || allChunks.length === 0) {\n      // Cloud parsing (chatbox-ai, mineru) resulted in 0 chunks - mark as done (truly empty file)\n      // Local parsing resulted in 0 chunks - mark as failed so user can retry with server parsing\n      if (parserConfig.type === 'chatbox-ai' || parserConfig.type === 'mineru') {\n        await db.execute({\n          sql: 'UPDATE kb_file SET chunk_count = 0, status = ? WHERE id = ?',\n          args: ['done', fileMeta.fileId],\n        })\n      } else {\n        throw new Error('No content extracted from file')\n      }\n      return\n    }\n\n    // Record total chunks if not already recorded\n    if (currentTotalChunks === 0 || currentTotalChunks !== allChunks.length) {\n      await db.execute({\n        sql: 'UPDATE kb_file SET total_chunks = ? WHERE id = ?',\n        args: [allChunks.length, fileMeta.fileId],\n      })\n      log.debug(`[FILE] Recorded total chunks: ${allChunks.length} for file ${fileMeta.fileId}`)\n    }\n\n    log.debug(`[FILE] Processing progress: ${currentChunkCount}/${allChunks.length} chunks already processed`)\n\n    // 3. Check if processing is already complete\n    if (currentChunkCount >= allChunks.length) {\n      log.info(`[FILE] File already fully processed: ${fileMeta.filename} (id=${fileMeta.fileId})`)","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/knowledge-base/file-loaders.ts#L112-L148","documentation":"Thrown by DeepSeek.listModels() when GET `https://api.deepseek.com/models` (hardcoded host) returns a body without a `data` field. DeepSeek's OpenAI-compatible API returns `{ data: [{ id, owned_by }] }`, so a missing `data` field means the request was rejected (auth/billing) or the hardcoded URL was redirected to a non-API response. The whole body is JSON.stringified into the message.","triggerScenarios":"Invalid or empty `apiKey` (DeepSeek returns `{ error: {...} }` with 401 but the body still parses without `data`); account has no balance / billing disabled (DeepSeek returns an error object); a transparent proxy or captive portal returns HTML that res.json() parses to `{}`; DeepSeek API is temporarily down and returns a JSON error envelope; the URL is intercepted by a custom DNS/firewall returning a different schema.","commonSituations":"New key not yet activated; key pasted with surrounding whitespace; free trial credit exhausted so the API rejects listing; running behind a corporate proxy that rewrites api.deepseek.com to an internal stub; user in a region where the endpoint is blocked and a CDN returns an error JSON.","solutions":["Read the JSON.stringify'd body in the ApiError message — DeepSeek's `{ error: { message: '...' } }` tells you whether it is auth, balance, or rate limit.","Top up DeepSeek balance and confirm the key is valid at platform.deepseek.com; verify the key has no leading/trailing space.","Check network egress: api.deepseek.com must be reachable and not rewritten by a proxy/captive portal.","If the host must be configurable, note this provider hardcodes the URL — switch to a Custom OpenAI provider pointing at DeepSeek so you can set apiHost and use a listModels path that your relay supports."],"exampleFix":"// before\nconst json = await res.json()\nif (!json.data) throw new ApiError(JSON.stringify(json))\n\n// after: surface the upstream error message\nif (!json.data) {\n  const msg = json?.error?.message || JSON.stringify(json)\n  throw new ApiError(`DeepSeek listModels failed: ${msg}`)\n}","handlingStrategy":"try-catch","validationCode":"async function probeDeepSeek(key: string): Promise<boolean> {\n  const res = await fetch('https://api.deepseek.com/models', { headers: { Authorization: `Bearer ${key}` } })\n  const json = await res.json().catch(() => ({}))\n  return Array.isArray((json as any)?.data)\n}","typeGuard":"function isDeepSeekModelsResponse(json: unknown): json is { data: { id: string; owned_by?: string }[] } {\n  return typeof json === 'object' && json !== null && Array.isArray((json as any).data)\n}","tryCatchPattern":"try { return await deepseek.listModels() }\ncatch (e) {\n  if (e instanceof ApiError) { console.warn('DeepSeek listModels body:', e.message); return [] }\n  throw e\n}","preventionTips":["Confirm DeepSeek account balance is positive before listing models.","Trim the API key when saving it.","Note this provider hardcodes the host — for custom hosts, use a Custom OpenAI provider pointing at DeepSeek."],"tags":["api","deepseek","models-list","configuration","auth"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}