{"record":{"id":"3b7154a90eb99b70","repo":"nexu-io/open-design","slug":"daemon-resp-status-on-url-body-resp-st","errorCode":null,"errorMessage":"daemon ${resp.status} on ${url}: ${body || resp.statusText}","messagePattern":"daemon (.+?) on (.+?): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/mcp.ts","lineNumber":3007,"sourceCode":"\n  const subs = list.filter((p) =>\n    String(p.name || '').toLowerCase().includes(lower),\n  );\n  if (subs.length === 1) { const p = subs[0]!; return { id: p.id, name: p.name, source: 'substring' as const }; }\n  if (subs.length > 1) {\n    const opts = subs.map((p) => `${p.name} (${p.id})`).join(', ');\n    throw new Error(\n      `multiple projects match \"${arg}\": ${opts}. Pass the UUID instead.`,\n    );\n  }\n  throw new Error(`no project matches \"${arg}\"`);\n}\n\nasync function getJson<T>(url: string, headers?: Record<string, string>): Promise<T> {\n  const resp = await fetch(url, headers ? { headers } : undefined);\n  if (!resp.ok) {\n    const body = await safeText(resp);\n    throw new Error(`daemon ${resp.status} on ${url}: ${body || resp.statusText}`);\n  }\n  return (await resp.json()) as T;\n}\n\nasync function getFile(\n  baseUrl: string,\n  project: string,\n  relPath: string,\n  active: ActiveContext | null,\n  resolved?: ResolvedProject | null,\n  offset = 0,\n  limit = 2000,\n  headers?: Record<string, string>,\n) {\n  const segments = String(relPath)\n    .split('/')\n    .filter((s) => s.length > 0)\n    .map(encodeURIComponent);","sourceCodeStart":2989,"sourceCodeEnd":3025,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/mcp.ts#L2989-L3025","documentation":"Thrown by the generic getJson helper when a GET request returns a non-2xx status. The message includes the HTTP status, the URL, and either the response body (via safeText) or statusText. This is the catch-all transport error for read operations in the MCP layer (active context, project list, etc.).","triggerScenarios":"Daemon returns 4xx/5xx (e.g. 401 auth, 404 route, 500 internal); proxy returns an error page; the URL hit a removed/renamed endpoint; rate limiting (429).","commonSituations":"Missing or expired auth headers; daemon version mismatch where an endpoint was removed; daemon crashed and a reverse proxy served 502; CORS/network appliance injecting an error.","solutions":["Read the embedded status code: 401/403 → fix auth headers; 404 → wrong URL or daemon version; 500 → check daemon logs; 429 → back off.","Run `pnpm tools-dev status --json` and `pnpm tools-dev logs --json` to inspect daemon health and errors.","Confirm the base URL and port match the running daemon.","Update the daemon and MCP client to matching versions if an endpoint moved.","For 5xx, restart the daemon via tools-dev and retry once."],"exampleFix":"// before: 401 from missing auth header\ngetJson(`${baseUrl}/api/active`)\n// after\ngetJson(`${baseUrl}/api/active`, { Authorization: `Bearer ${token}` })","handlingStrategy":"try-catch","validationCode":"async function safeGetJson<T>(url: string, headers?: Record<string,string>): Promise<T> {\n  const resp = await fetch(url, headers ? { headers } : undefined);\n  if (!resp.ok) throw new Error(`daemon ${resp.status}`);\n  return await resp.json() as T;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await getJson(url, headers);\n} catch (e) {\n  const msg = String((e as Error).message);\n  if (/daemon 401|403/.test(msg)) await refreshAuth();\n  if (/daemon 5\\d\\d/.test(msg)) await waitForDaemon();\n  throw e;\n}","preventionTips":["Wrap daemon reads in a helper that inspects status codes.","Keep auth headers fresh and the daemon version matched to the client.","Surface the embedded status/body to your logs for faster triage."],"tags":["mcp","network","http","daemon"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}