{"record":{"id":"90ec7dbbd577c315","repo":"mudler/LocalAI","slug":"http-response-status","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"core/http/react-ui/src/hooks/useChat.js","lineNumber":372,"sourceCode":"    maxTpsRef.current = 0\n\n    let usage = {}\n    const newMessages = [] // Accumulate messages to add to history\n\n    if (activeChat.mcpMode && !hasMcpServers) {\n      // Legacy MCP SSE streaming (custom event types from /v1/mcp/chat/completions)\n      try {\n        const timeoutId = setTimeout(() => controller.abort(), 300000) // 5 min timeout\n        const response = await fetch(apiUrl(endpoint), {\n          method: 'POST',\n          headers: { 'Content-Type': 'application/json' },\n          body: JSON.stringify(requestBody),\n          signal: controller.signal,\n        })\n        clearTimeout(timeoutId)\n\n        if (!response.ok) {\n          throw new Error(await extractHttpError(response))\n        }\n\n        const reader = response.body.pipeThrough(new TextDecoderStream()).getReader()\n        let buffer = ''\n        let assistantContent = ''\n        let reasoningContent = ''\n        let hasReasoningFromAPI = false\n        let currentToolCalls = []\n\n        while (true) {\n          const { value, done } = await reader.read()\n          if (done) break\n\n          buffer += value\n          const lines = buffer.split('\\n')\n          buffer = lines.pop() || ''\n\n          for (const line of lines) {","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/mudler/LocalAI/blob/44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26/core/http/react-ui/src/hooks/useChat.js#L354-L390","documentation":"Thrown by useChat.js when the legacy MCP SSE streaming POST to the /v1/mcp/chat/completions endpoint returns a non-2xx status. The raw HTTP status/message is only the fallback — extractHttpError(response) is called first to pull a structured error message out of the response body, so the thrown Error usually carries the server's own message (e.g. 'model not found'). This is the chat-modality request path for MCP tool-driven conversations.","triggerScenarios":"POSTing a chat completion with MCP tools attached to /v1/mcp/chat/completions where the server rejects it: unknown model name in the request body, model not loaded, missing/invalid auth for the endpoint, or a backend error during tool execution. The 5-minute abort timer (setTimeout controller.abort 300000) can also surface here as an AbortError, but the HTTP error path is specifically a non-ok status.","commonSituations":"Model name typo or model not pulled/loaded in LocalAI; API key required by the server but not configured in the UI; backend crash mid-request; hitting an older LocalAI build that lacks the /v1/mcp endpoint; CORS/proxy returning 502 in front of LocalAI.","solutions":["Read the actual message: the Error text is extracted from the response JSON (extractHttpError), which names the real cause — act on that first","Verify the model exists and is loaded: GET /v1/models in the same UI, and pull/load it if absent","Check auth: if the server runs with an API key, configure it in the UI settings so the request carries it","Confirm the LocalAI version serves /v1/mcp/chat/completions (legacy path) — upgrade or switch to the non-legacy streaming path if not"],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(await extractHttpError(response))\n}\n\n// after — the throw already extracts the server message; surface it fully to the user\ncatch (err) {\n  setError(err.message || `HTTP request failed`)\n  addToast(err.message, 'error')\n}","handlingStrategy":"try-catch","validationCode":"// verify model availability before opening the SSE stream\nconst models = await modelsApi.listV1()\nif (!models.data?.some((m) => m.id === requestBody.model)) throw new Error(`unknown model ${requestBody.model}`)","typeGuard":null,"tryCatchPattern":"try {\n  const response = await fetch(apiUrl(endpoint), ...)\n  if (!response.ok) throw new Error(await extractHttpError(response))\n} catch (err) {\n  if (err.name === 'AbortError') setError('request timed out')\n  else setError(err.message)\n}","preventionTips":["Pre-check the model is loaded via GET /v1/models before starting an MCP chat","Keep the UI's API key setting in sync with the server's auth requirement","Distinguish AbortError (5-min timeout) from HTTP errors in the catch"],"tags":["http","mcp","chat-completions","streaming","localai-api"],"backgroundTag":null,"analyzedSha":"44413a9d06bf5bc52ce088ba8ca74e5a2e8bee26","analyzedAt":"2026-08-15T10:13:50.291Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}