{"record":{"id":"2a9527a77fb5f72f","repo":"chatboxai/chatbox","slug":"invalid-pagination-parameters","errorCode":null,"errorMessage":"Invalid pagination parameters","messagePattern":"Invalid pagination parameters","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/knowledge-base/ipc-handlers.ts","lineNumber":332,"sourceCode":"      sentry.withScope((scope) => {\n        scope.setTag('component', 'knowledge-base-ipc')\n        scope.setTag('operation', 'file_count')\n        scope.setExtra('kbId', kbId)\n        sentry.captureException(error)\n      })\n      throw error\n    }\n  })\n\n  ipcMain.handle('kb:file:list-paginated', async (_event, kbId: number, offset = 0, limit = 20) => {\n    try {\n      // log.debug(`ipcMain: kb:file:list-paginated, kbId=${kbId}, offset=${offset}, limit=${limit}`)\n\n      if (!kbId || kbId <= 0) {\n        throw new Error('Invalid knowledge base ID')\n      }\n      if (offset < 0 || limit <= 0 || limit > 100) {\n        throw new Error('Invalid pagination parameters')\n      }\n\n      const db = getDatabase()\n      const rs = await db.execute({\n        sql: 'SELECT * FROM kb_file WHERE kb_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ?',\n        args: [kbId, limit, offset],\n      })\n      return rs.rows.map((row) => ({\n        id: row.id,\n        kb_id: row.kb_id,\n        filename: row.filename,\n        filepath: row.filepath,\n        mime_type: row.mime_type,\n        file_size: row.file_size || 0,\n        chunk_count: row.chunk_count || 0,\n        total_chunks: row.total_chunks || 0,\n        status: row.status,\n        error: row.error,","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/main/knowledge-base/ipc-handlers.ts#L314-L350","documentation":"Thrown by authenticatedAfetch for any non-2xx, non-401 response on the first attempt (the 401 branch is handled separately above). Identical construction to the unauthenticated afetch ApiError (status + sanitised body + responseBody + statusCode + requestId), but emitted from the authenticated wrapper. This is the catch-all HTTP error for authenticated requests that are not auth-related.","triggerScenarios":"Authenticated request returns 400 (bad params), 403 (forbidden — permissions/feature not entitled), 404 (endpoint/resource missing), 429 (rate limit / quota exhausted on Chatbox AI), 5xx (Chatbox AI backend error, gateway HTML). The Chatbox-remote-error parser runs first and may convert known codes into a ChatboxAIAPIError; only unmapped errors reach this throw.","commonSituations":"Chatbox AI quota exhausted but the error code is not in the codeNameMap; feature not entitled for the user's plan (403); malformed request body (400); backend outage (502/503 HTML, sanitised); deprecated endpoint returning 404 after an API revision; rate limiting on bursty agent loops.","solutions":["Read `error.statusCode`: 403 → entitlement/plan issue, 429 → slow down or surface the quota UI, 4xx other → fix the request payload, 5xx → transient retry.","If `options.parseChatboxRemoteError` was enabled and you still got a raw ApiError, the backend error code is not in ChatboxAIAPIError.codeNameMap — report it so it gets a dedicated i18n key.","Use `error.requestId` when contacting support / correlating server logs.","For 5xx, retry with backoff at the caller; for 4xx (except 429), do not retry."],"exampleFix":"// before\nconst res = await authedAfetch(url, init, { parseChatboxRemoteError: true })\n\n// after\ntry { const res = await authedAfetch(url, init, { parseChatboxRemoteError: true }) }\ncatch (e) {\n  if (e instanceof ChatboxAIAPIError) showI18nError(e.detail.i18nKey)\n  else if (e instanceof ApiError && e.statusCode === 429) showQuotaUI()\n  throw e\n}","handlingStrategy":"try-catch","validationCode":"function classifyAuthedError(e: ApiError): 'auth' | 'quota' | 'client' | 'server' {\n  if (e.statusCode === 401 || e.statusCode === 403) return 'auth'\n  if (e.statusCode === 429) return 'quota'\n  if (e.statusCode && e.statusCode >= 500) return 'server'\n  return 'client'\n}","typeGuard":"import { ApiError, ChatboxAIAPIError } from '../models/errors'\nfunction isApiError(e: unknown): e is ApiError { return e instanceof ApiError }","tryCatchPattern":"try { return await authedAfetch(url, init, { parseChatboxRemoteError: true }) }\ncatch (e) {\n  if (e instanceof ChatboxAIAPIError) showI18nError(e.detail.i18nKey)\n  else if (e instanceof ApiError && e.statusCode === 429) showQuotaUI()\n  else if (e instanceof ApiError && e.statusCode && e.statusCode >= 500) await backoff()\n  throw e\n}","preventionTips":["Always pass parseChatboxRemoteError: true for Chatbox AI endpoints so known codes get i18n messages.","Map new backend codes into ChatboxAIAPIError.codeNameMap so they stop reaching this generic throw.","Use requestId when filing support tickets."],"tags":["auth","http","chatbox-ai","request-layer","quota","provider"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}