{"record":{"id":"368c0807983b3c31","repo":"datawhalechina/hello-agents","slug":"http-response-status","errorCode":null,"errorMessage":"HTTP ${response.status}","messagePattern":"HTTP \\$\\{response\\.status\\}","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"Co-creation-projects/huailishang-AgentPlatformBase/frontend/app.js","lineNumber":28,"sourceCode":"  agentList: document.getElementById(\"agentList\"),\n  chatForm: document.getElementById(\"chatForm\"),\n  messageInput: document.getElementById(\"messageInput\"),\n  mentionMenu: document.getElementById(\"mentionMenu\"),\n  messages: document.getElementById(\"messages\"),\n  statusText: document.getElementById(\"statusText\"),\n  refreshButton: document.getElementById(\"refreshButton\"),\n  taskView: document.getElementById(\"taskView\"),\n  eventList: document.getElementById(\"eventList\"),\n};\n\nasync function api(path, options = {}) {\n  const response = await fetch(path, {\n    headers: { \"Content-Type\": \"application/json\", ...(options.headers || {}) },\n    ...options,\n  });\n  if (!response.ok) {\n    const text = await response.text();\n    throw new Error(text || `HTTP ${response.status}`);\n  }\n  return response.json();\n}\n\nfunction nowText() {\n  return new Date().toLocaleTimeString(\"zh-CN\", { hour: \"2-digit\", minute: \"2-digit\" });\n}\n\nfunction escapeHtml(value) {\n  return String(value)\n    .replaceAll(\"&\", \"&amp;\")\n    .replaceAll(\"<\", \"&lt;\")\n    .replaceAll(\">\", \"&gt;\")\n    .replaceAll('\"', \"&quot;\")\n    .replaceAll(\"'\", \"&#039;\");\n}\n\nfunction linkify(text) {","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/huailishang-AgentPlatformBase/frontend/app.js#L10-L46","documentation":"Single api() wrapper for the whole task-view frontend: fetch(path) with JSON headers, and on !response.ok it reads the body as text and throws that text, falling back to 'HTTP <status>' only when the body is empty. Because it throws raw body text, HTML error pages get thrown as-is; also, if the server responds with HTML for a 200 (login redirect page), response.json() will throw a SyntaxError instead.","triggerScenarios":"Any backend 4xx/5xx on the relative path: FastAPI 422 detail JSON stringified as raw text, proxy 502 HTML pages thrown wholesale, session expiry returning a redirect page. Relative paths assume the frontend and API share an origin.","commonSituations":"Frontend served statically while API sits on another origin/port (path resolves against the wrong origin → 404 HTML); auth cookie expired so every call returns a 401/redirect page; nginx misroute.","solutions":["Check DevTools network tab for the failing path and actual response; fix routing/proxy so API paths hit the backend origin","Parse the text as JSON when content-type is application/json and extract detail/message for a cleaner error","Truncate long HTML bodies in the thrown message","If auth-based, detect 401 and redirect to login"],"exampleFix":"// before\nif (!response.ok) {\n    const text = await response.text();\n    throw new Error(text || `HTTP ${response.status}`);\n}\n\n// after\nif (!response.ok) {\n    const text = await response.text().catch(() => '');\n    let msg = text.slice(0, 200);\n    try { const j = JSON.parse(text); msg = j.detail || j.message || msg; } catch (_) {}\n    throw new Error(msg || `HTTP ${response.status}`);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isJsonResponse(resp) { return (resp.headers.get('content-type') || '').includes('application/json'); }","tryCatchPattern":"try { const data = await api('/api/tasks'); } catch (e) { if (/401|403/.test(e.message)) location.href = '/login'; else renderError(e.message.slice(0, 200)); }","preventionTips":["Serve frontend and API behind one origin/proxy so relative paths resolve","Parse JSON error bodies before throwing","Truncate HTML error pages in thrown messages","Check content-type before response.json()"],"tags":["fetch","http","relative-url","error-handling"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}