{"record":{"id":"a5ebfb8577a43167","repo":"odysseus-dev/odysseus","slug":"http-res-status-a5ebfb","errorCode":null,"errorMessage":"HTTP ${res.status}","messagePattern":"HTTP \\$\\{res\\.status\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"static/js/cookbookServe.js","lineNumber":837,"sourceCode":"    vllm: 'vllm',\n    sglang: 'sglang',\n    llamacpp: 'llama_cpp',\n    mlx: 'mlx_lm',\n    mlx_image: 'mflux',\n    diffusers: 'diffusers',\n  };\n  const packageName = _dependencyPkgForServeBackend(backend, repo) || packageByBackend[backend];\n  if (!packageName) return null;\n  const target = _selectedServeTarget(panel);\n  const params = new URLSearchParams();\n  if (target.host) {\n    params.set('host', target.host);\n    if (target.port) params.set('ssh_port', target.port);\n    if (target.venv) params.set('venv', target.venv);\n  }\n  if (repo) params.set('model_hint', repo);\n  const res = await fetch('/api/cookbook/packages' + (params.toString() ? '?' + params.toString() : ''), { credentials: 'same-origin' });\n  if (!res.ok) throw new Error(`HTTP ${res.status}`);\n  const data = await res.json();\n  const pkg = (data.packages || []).find(p => p.name === packageName);\n  return { pkg, target };\n}\n\nfunction _runtimeNoteText(backend, pkg, target) {\n  const labels = { vllm: 'vLLM', sglang: 'SGLang', llamacpp: 'llama.cpp', mlx: 'MLX', mlx_image: 'MLX Image', diffusers: 'Diffusers' };\n  const label = labels[backend] || backend;\n  if (!pkg) return `${label} readiness unavailable for ${target.label}.`;\n  const note = pkg.status_note || pkg.update_note || '';\n  if (pkg.installed === null || pkg.probe_error) {\n    return note ? `${label} readiness unavailable for ${target.label}: ${note}` : `${label} readiness unavailable for ${target.label}.`;\n  }\n  if (pkg.installed) {\n    return note ? `${label} ready on ${target.label}: ${note}` : `${label} ready on ${target.label}.`;\n  }\n  return note ? `${label} missing on ${target.label}: ${note}` : `${label} missing on ${target.label}.`;\n}","sourceCodeStart":819,"sourceCodeEnd":855,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/static/js/cookbookServe.js#L819-L855","documentation":"Error thrown when fetching cookbook package readiness: GET /api/cookbook/packages (optionally with host/ssh_port/venv/model_hint query params) returns non-OK, aborting the serve-runtime readiness note computation. Only 'HTTP <status>' is surfaced.","triggerScenarios":"GET /api/cookbook/packages returns 500 (probe of the remote host crashed — ssh failure, venv path invalid), 422 (malformed port), or 404 after a route change; when host is set the backend shells out over SSH and any unhandled ssh error becomes a 500.","commonSituations":"SSH target unreachable or credentials rejected so the package probe throws server-side; venv parameter points to a non-existent directory; backend/frontend version skew renaming the route; slow SSH probes hitting a proxy timeout.","solutions":["curl -i '/api/cookbook/packages?host=...&ssh_port=...&venv=...' with the same params to see the real error body","Verify the SSH target manually: ssh -p <port> <host> true","Check the venv path exists on the target","Inspect backend logs for the exception behind the 500"],"exampleFix":"// before\nif (!res.ok) throw new Error(`HTTP ${res.status}`);\n\n// after\nif (!res.ok) {\n  const body = await res.text().catch(() => '');\n  let msg = '';\n  try { msg = JSON.parse(body).detail || ''; } catch { msg = body.slice(0, 160); }\n  throw new Error(`HTTP ${res.status}${msg ? ': ' + msg : ''}`);\n}","handlingStrategy":"try-catch","validationCode":"if (target.host && !target.host.includes('@')) return null; // probe needs user@host","typeGuard":null,"tryCatchPattern":"try {\n  const res = await fetch('/api/cookbook/packages' + (params.toString() ? '?' + params.toString() : ''), { credentials: 'same-origin' });\n  if (!res.ok) {\n    const body = await res.text().catch(() => '');\n    let msg = '';\n    try { msg = JSON.parse(body).detail || ''; } catch { msg = body.slice(0, 160); }\n    throw new Error(`HTTP ${res.status}${msg ? ': ' + msg : ''}`);\n  }\n  const data = await res.json();\n  return { pkg: (data.packages || []).find(p => p.name === packageName), target };\n} catch (e) {\n  return { pkg: null, target }; // callers already render 'readiness unavailable' for missing pkg\n}","preventionTips":["Degrade gracefully: a failed readiness probe should show 'unavailable', not break the serve panel","Add a short timeout (AbortSignal.timeout) around SSH-backed probes so they can't hang the UI","Log the full body server-side; surface only detail to users"],"tags":["fetch","http-error","ssh","package-probe","cookbook"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}