{"record":{"id":"402ba28006e29b74","repo":"AlexsJones/llmfit","slug":"request-failed-with-status-response-status","errorCode":null,"errorMessage":"Request failed with status ${response.status}.","messagePattern":"Request failed with status (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"llmfit-web/src/api.js","lineNumber":115,"sourceCode":"      params.set('max_context', String(parsed));\n    }\n  }\n\n  appendSimulationParams(params, simulation);\n  return params.toString();\n}\n\nasync function parseJsonOrThrow(response) {\n  let payload;\n  try {\n    payload = await response.json();\n  } catch (err) {\n    throw new Error('Server returned an invalid JSON response.');\n  }\n\n  if (!response.ok) {\n    const message = payload?.error || `Request failed with status ${response.status}.`;\n    throw new Error(message);\n  }\n\n  return payload;\n}\n\nexport async function fetchSystemInfo(simulation = {}, signal) {\n  const query = appendSimulationParams(new URLSearchParams(), simulation).toString();\n  const path = query ? `/api/v1/system?${query}` : '/api/v1/system';\n  const response = await fetch(path, { signal });\n  return parseJsonOrThrow(response);\n}\n\nexport async function fetchModels(filters, simulation = {}, signal) {\n  const query = buildModelsQuery(filters, simulation);\n  const path = query ? `/api/v1/models?${query}` : '/api/v1/models';\n  const response = await fetch(path, { signal });\n  return parseJsonOrThrow(response);\n}","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/AlexsJones/llmfit/blob/a9ac7ed91c1729cd93944bc1338e8313daaba8fa/llmfit-web/src/api.js#L97-L133","documentation":"Fallback branch of parseJsonOrThrow(): the response had a non-2xx status (response.ok === false) AND the parsed JSON body had no `error` field, so the generic status line is used. It is the llmfit web client's way of surfacing an HTTP-level failure from /api/v1/system, /api/v1/models, /api/v1/runtimes, /api/v1/installed, /api/v1/download, /api/v1/download/{id}/status, or /api/v1/plan when the server did not attach a structured message.","triggerScenarios":"POST /api/v1/download with a model or runtime the server rejects without an error body; GET /api/v1/download/{id}/status after the server restarted and lost the download id; POST /api/v1/plan with malformed context/quant values; any 404/405/500 from the Axum router whose body is empty or lacks payload.error.","commonSituations":"Passing a model name that is not in the catalog; polling download status with a stale id after a server restart; query params rejected by server-side validation; a handler panicking (500) before it can build a JSON error response.","solutions":["Wrap the call and log response.status alongside the message so you know which HTTP code fired it.","Check the llmfit server logs at the same timestamp - a 4xx/5xx there explains the missing body.","Validate the arguments you pass (model exists in /api/v1/models, download id came from a live startDownload response).","If you control the server code, make failing handlers return `{\"error\": \"...\"}` so clients get the specific message instead of this fallback."],"exampleFix":"// before\nconst response = await fetch(`/api/v1/download/${encodeURIComponent(id)}/status`, { signal });\nreturn parseJsonOrThrow(response);\n\n// after - surface status and body when the server omits payload.error\nconst response = await fetch(`/api/v1/download/${encodeURIComponent(id)}/status`, { signal });\nif (!response.ok) {\n  const body = await response.text();\n  throw new Error(`Status ${response.status} for download ${id}: ${body.slice(0, 200) || '(empty body)'}`);\n}\nreturn response.json();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await fetchDownloadStatus(id, signal);\n} catch (err) {\n  const m = err.message.match(/^Request failed with status (\\d+)\\.$/);\n  if (m) {\n    const status = Number(m[1]);\n    if (status === 404) return null;          // unknown/expired download id -> treat as gone\n    if (status >= 500) throw new Error('llmfit server error, retry later');\n  }\n  throw err;\n}","preventionTips":["Validate model names against a prior fetchModels() result before calling startDownload().","Treat download ids as ephemeral: re-run startDownload after a server restart instead of polling stale ids.","Send well-formed query params (numbers as numbers) so the server can answer with its structured payload.error instead of the generic fallback.","Log response status together with the endpoint name so HTTP failures are attributable."],"tags":["http","api","frontend","status-code"],"backgroundTag":"http-error-status","analyzedSha":"a9ac7ed91c1729cd93944bc1338e8313daaba8fa","analyzedAt":"2026-08-16T19:19:11.438Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}