{"record":{"id":"731a6d702e418014","repo":"chatboxai/chatbox","slug":"knowledge-base-name-cannot-be-empty","errorCode":null,"errorMessage":"Knowledge base name cannot be empty","messagePattern":"Knowledge base name cannot be empty","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/knowledge-base/ipc-handlers.ts","lineNumber":170,"sourceCode":"    ) => {\n      try {\n        log.info(`ipcMain: kb:update, id=${id}, name=${name}, rerankModel=${rerankModel}, visionModel=${visionModel}`)\n\n        if (!id || id <= 0) {\n          throw new Error('Invalid knowledge base ID')\n        }\n\n        if (!name && rerankModel === undefined && visionModel === undefined) {\n          return 0\n        }\n\n        const db = getDatabase()\n        let sql = 'UPDATE knowledge_base SET '\n        const args: (string | number)[] = []\n\n        if (name !== undefined) {\n          if (!name.trim()) {\n            throw new Error('Knowledge base name cannot be empty')\n          }\n          sql += 'name = ?'\n          args.push(name.trim())\n        }\n        if (rerankModel !== undefined) {\n          if (args.length > 0) sql += ', '\n          sql += 'rerank_model = ?'\n          args.push(rerankModel ?? '')\n        }\n        if (visionModel !== undefined) {\n          if (args.length > 0) sql += ', '\n          sql += 'vision_model = ?'\n          args.push(visionModel ?? '')\n        }\n        sql += ' WHERE id = ?'\n        args.push(id)\n\n        const rs = await db.execute(sql, args)","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/knowledge-base/ipc-handlers.ts#L152-L188","documentation":"Thrown by afetch() (the unauthenticated fetch wrapper) when the response status is not in 2xx after all retries are exhausted within the loop, OR on the first non-ok response when retry=0. The message embeds `res.status` and a sanitised body (HTML error pages from nginx/cloudflare are replaced with the standard HTTP status text). Carries responseBody, statusCode, and requestId. Used by apiRequest for all non-authenticated provider calls.","triggerScenarios":"Provider returns 401/403 (bad/expired API key) with a JSON error body; 429 rate limit from the upstream; 5xx from the provider or an intermediate gateway (502/503/504 returning HTML); 404 because the apiHost + path is wrong; CORS preflight rejected in the browser renderer; network returns a captive-portal HTML page.","commonSituations":"Wrong API key → 401; usage spikes past provider rate limit → 429; provider outage → 503 HTML (sanitised to 'Service Unavailable'); misconfigured apiHost pointing to a path that 404s; browser build hitting a provider without CORS headers; self-hosted gateway returning nginx error pages.","solutions":["Read `error.statusCode` first: 401/403 → fix key, 429 → slow down / upgrade plan, 5xx → transient, retry later, 404 → check apiHost/path.","Inspect `error.responseBody` for the provider's structured error (HTML pages are already sanitised in the message but the raw body may still be present).","For 5xx, retry with exponential backoff by passing a higher `retry` option to afetch; for 4xx (except 429) retrying will not help.","For browser/renderer 5xx HTML responses, the message is already collapsed to the HTTP status text — confirm the provider endpoint and CORS config."],"exampleFix":"// before\nconst res = await apiRequest({ url, method: 'POST', body })\n\n// after\ntry {\n  const res = await apiRequest({ url, method: 'POST', body, retry: 2 })\n} catch (e) {\n  if (e instanceof ApiError && e.statusCode === 429) await backoff()\n  if (e instanceof ApiError && e.statusCode && e.statusCode >= 500) retryLater()\n  throw e\n}","handlingStrategy":"retry","validationCode":"function isRetryableStatus(status?: number): boolean {\n  return status === 429 || (typeof status === 'number' && status >= 500)\n}","typeGuard":"import { ApiError } from '../models/errors'\nfunction isApiError(e: unknown): e is ApiError {\n  return e instanceof ApiError && typeof e.statusCode === 'number'\n}","tryCatchPattern":"try { return await afetch(url, init, { retry: 2 }) }\ncatch (e) {\n  if (e instanceof ApiError && isRetryableStatus(e.statusCode)) await backoff()\n  if (e instanceof ApiError && e.statusCode === 401) promptForApiKey()\n  throw e\n}","preventionTips":["Pass a retry count for transient (5xx/429) endpoints; skip retries for 4xx (except 429).","Read statusCode and responseBody before deciding to retry or surface to the user.","Sanitize upstream HTML bodies yourself if you display the message (the lib already does this)."],"tags":["network","http","api","retry","provider","request-layer"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}