{"record":{"id":"ade31fc7db8597f5","repo":"mudler/LocalAI","slug":"http-response-status-ade31f","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/http/react-ui/src/utils/api.js","lineNumber":76,"sourceCode":"  })\n}\n\n// SSE streaming for chat completions\nexport async function streamChat(body, signal) {\n  const response = await fetch(apiUrl(API_CONFIG.endpoints.chatCompletions), {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({ ...body, stream: true }),\n    signal,\n  })\n\n  if (!response.ok) {\n    let errorMessage = `HTTP ${response.status}`\n    try {\n      const data = await response.json()\n      if (data?.error?.message) errorMessage = data.error.message\n    } catch (_e) { /* not JSON */ }\n    throw new Error(errorMessage)\n  }\n\n  return response.body\n}\n\n// Models API\nexport const modelsApi = {\n  list: (params) => fetchJSON(buildUrl(API_CONFIG.endpoints.models, params)),\n  listV1: () => fetchJSON(API_CONFIG.endpoints.modelsList),\n  listCapabilities: () => fetchJSON(API_CONFIG.endpoints.modelsCapabilities),\n  listAliases: () => fetchJSON(API_CONFIG.endpoints.modelsAliases),\n  // variant is optional. Omitting it lets the server auto-select the best\n  // build for this host, which is what the listing's auto_variant predicted.\n  install: (id, variant) => postJSON(\n    variant\n      ? `${API_CONFIG.endpoints.installModel(id)}?variant=${encodeURIComponent(variant)}`\n      : API_CONFIG.endpoints.installModel(id),\n    {}","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/http/react-ui/src/utils/api.js#L58-L94","documentation":"Fallback error from the streaming fetch helper in utils/api.js used for chat completions: when the response is not ok, it tries to parse the body as JSON and use data.error.message; if the body is not JSON (or has no error.message), the generic 'HTTP <status>' is thrown. This helper always forces stream:true in the body, so it is the shared entry point for all streaming chat/tts/image requests from the React UI.","triggerScenarios":"Any streamFetch call (chat completions with stream:true, etc.) receiving a non-2xx whose body is HTML (proxy error page), plain text, or empty — common with 502/503 from reverse proxies, 404 from a wrong API base path, or 401 when the server requires an API key. The JSON parse succeeds only for LocalAI-style {error:{message}} bodies.","commonSituations":"Reverse proxy (nginx/traefik) in front of LocalAI returning an HTML 502 page; wrong API_PROXY/base URL configured in the UI so /v1/... hits a static file server (404); missing Authorization header on a key-protected instance; LocalAI mid-restart returning connection resets.","solutions":["Check the status number: 401/403 → configure the API key in UI settings; 404 → fix the API base URL; 502/503 → proxy/upstream health","Reproduce with curl to see the raw body: curl -i -X POST $BASE/v1/chat/completions -H 'Content-Type: application/json' -d '{...}'","Ensure the model exists in GET /v1/models before requesting it","If the server is restarting, wait for /healthz then retry"],"exampleFix":"// before\nlet errorMessage = `HTTP ${response.status}`\ntry {\n  const data = await response.json()\n  if (data?.error?.message) errorMessage = data.error.message\n} catch (_e) { /* not JSON */ }\n\n// after — also surface text bodies for better diagnosis\nlet errorMessage = `HTTP ${response.status}`\nconst text = await response.text()\ntry {\n  const data = JSON.parse(text)\n  if (data?.error?.message) errorMessage = data.error.message\n} catch (_e) { if (text) errorMessage = text.slice(0, 200) }","handlingStrategy":"try-catch","validationCode":"// confirm base URL and auth before streaming\nconst probe = await fetch(apiUrl('/healthz'))\nif (!probe.ok) throw new Error('server unreachable — check API base URL')","typeGuard":null,"tryCatchPattern":"try { return await streamFetch(url, body, signal) } catch (e) {\n  if (/^HTTP 5/.test(e.message)) throw new Error(`upstream unavailable (${e.message}) — retry shortly`)\n  throw e\n}","preventionTips":["Validate the configured API base URL with a health probe on settings save","Attach the API key header uniformly in apiUrl/fetch wrappers so streams are never unauthenticated","Surfacing text bodies (not just JSON) makes proxy 502 pages diagnosable"],"tags":["http","streaming","fetch-wrapper","error-handling","localai-api"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}