{"record":{"id":"c950d57c1b565a83","repo":"janhq/jan","slug":"api-request-failed-with-status-response-status","errorCode":null,"errorMessage":"API request failed with status ${response.status}: ${JSON.stringify(errorData)}","messagePattern":"API request failed with status (.+?): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/llamacpp-extension/src/index.ts","lineNumber":3657,"sourceCode":"        combinedController.abort(abortController.signal.reason)\n      } else {\n        abortController.signal.addEventListener(\n          'abort',\n          () => combinedController.abort(abortController.signal.reason),\n          { once: true }\n        )\n      }\n    }\n    const response = await fetch(url, {\n      method: 'POST',\n      headers,\n      body,\n      connectTimeout: Number(this.timeout) * 1000, // default 10 minutes\n      signal: combinedController.signal,\n    }).finally(() => clearTimeout(timeoutId))\n    if (!response.ok) {\n      const errorData = await response.json().catch(() => null)\n      throw new Error(\n        `API request failed with status ${response.status}: ${JSON.stringify(\n          errorData\n        )}`\n      )\n    }\n\n    if (!response.body) {\n      throw new Error('Response body is null')\n    }\n\n    const reader = response.body.getReader()\n    const decoder = new TextDecoder('utf-8')\n    let buffer = ''\n    let jsonStr = ''\n    try {\n      while (true) {\n        const { done, value } = await reader.read()\n","sourceCodeStart":3639,"sourceCodeEnd":3675,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/llamacpp-extension/src/index.ts#L3639-L3675","documentation":"Thrown by handleStreamingResponse() when the streaming POST to the router's /v1/chat/completions returns a non-2xx status. The response body is parsed as JSON (errorData) and embedded verbatim into the message. This wraps any HTTP-layer failure from the llama.cpp server (model error, bad request, internal panic, timeout-abort) into a single descriptive error for the streaming path.","triggerScenarios":"Model OOM / context too long (HTTP 500 from llama-server). Malformed request body (400). The router aborted (503). The combined AbortController fired (timeout or user cancel) producing a non-ok response. Authentication failure if api_key was wrong. Server panicked on an unsupported sampling/template parameter.","commonSituations":"Context window exceeded for the loaded model. Prompt template/chat_template kwargs sent by the client are incompatible with the model. Sampling params (e.g. min_p with an old backend) unsupported. Timeout too short for a long generation. The combined signal aborted mid-flight due to the user stopping generation.","solutions":["Read errorData in the message - it usually contains the llama-server error string telling you the exact cause (OOM, bad param, etc.).","If OOM/context-too-long: reduce context length, unload other models, or use a smaller quant.","If 400 bad request: inspect the request body for unsupported keys (template_kwargs, sampling) against your llama.cpp version.","If timeout/abort: raise this.timeout or avoid aborting unless intentional.","If 503/server panic: capture router logs, restart the router, and report the panic."],"exampleFix":"// before\nconst stream = await provider.chat(opts, ac) // throws on HTTP 500\n// after - surface server's error and retry on context error\ntry { for await (const c of await provider.chat(opts, ac)) yield c }\ncatch (e) {\n  const m = String(e)\n  if (/context.+length|too long/i.test(m)) { opts.ctx_size = Math.min((opts.ctx_size ?? 4096) * 2, 32768); /* retry */ }\n  else throw e\n}","handlingStrategy":"try-catch","validationCode":"// Pre-validate request shape before sending (catch common 400s)\nfunction validateChatOpts(opts: any) {\n  if (typeof opts.model !== 'string' || !opts.model) throw new Error('opts.model required')\n  if (opts.messages && !Array.isArray(opts.messages)) throw new Error('opts.messages must be an array')\n  if (opts.max_tokens != null && (!Number.isFinite(opts.max_tokens) || opts.max_tokens <= 0)) throw new Error('bad max_tokens')\n}\nvalidateChatOpts(opts)","typeGuard":"function isChatCompletionRequest(x: unknown): x is { model: string; messages: unknown[] } {\n  return typeof (x as any)?.model === 'string' && Array.isArray((x as any)?.messages)\n}","tryCatchPattern":"try { for await (const c of await provider.chat(opts, ac)) emit(c) }\ncatch (e) {\n  const m = String(e)\n  if (/status 4\\d\\d/.test(m)) { /* fix request from errorData, do not retry blindly */ throw new Error('bad request: ' + m) }\n  if (/status 5\\d\\d|timed out/i.test(m)) { /* transient - one retry */ }\n  else throw e\n}","preventionTips":["Bound max_tokens and ctx_size to the loaded model's capacity before requesting.","Validate the request body schema client-side to eliminate 400s.","Set a generous timeout and only abort intentionally; surface abort vs server error distinctly."],"tags":["chat","streaming","http","network","llama-server","inference"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}