{"record":{"id":"7454ec5ced379781","repo":"datawhalechina/hello-agents","slug":"stream-request-failed-resp-status-message","errorCode":null,"errorMessage":"stream request failed: ${resp.status} ${message}","messagePattern":"stream request failed: (.+?) (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"Co-creation-projects/monkeyhlj-NetworkHealthReportAgent/frontend/src/api.js","lineNumber":100,"sourceCode":"\nexport async function askQuestionStream(question, startDate, endDate, siteId = null, onChunk) {\n  const base = await resolveBase()\n  const resp = await fetch(`${base}/chat/stream`, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json'\n    },\n    body: JSON.stringify({\n      question,\n      start_date: startDate,\n      end_date: endDate,\n      site_id: siteId\n    })\n  })\n\n  if (!resp.ok || !resp.body) {\n    const message = await resp.text()\n    throw new Error(`stream request failed: ${resp.status} ${message}`)\n  }\n\n  const meta = {\n    llmEnabled: resp.headers.get('X-Agent-LLM') === 'true',\n    mcpEnabled: resp.headers.get('X-Agent-MCP') === 'true',\n    reportIntent: resp.headers.get('X-Agent-Report-Intent') === 'true',\n    artifactUrl: resp.headers.get('X-Agent-Artifact-Url') || '',\n    artifactName: resp.headers.get('X-Agent-Artifact-Name') || ''\n  }\n\n  const reader = resp.body.getReader()\n  const decoder = new TextDecoder('utf-8')\n\n  while (true) {\n    const { done, value } = await reader.read()\n    if (done) {\n      break\n    }","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/monkeyhlj-NetworkHealthReportAgent/frontend/src/api.js#L82-L118","documentation":"Streaming-report client: POSTs {question, start_date, end_date, site_id} to a report endpoint and requires both resp.ok AND resp.body; on failure it reads the body text and throws 'stream request failed: <status> <body>'. It then reads agent metadata from X-Agent-* response headers, so any interceptor that strips headers also breaks features downstream — but the throw itself is the HTTP/stream failure.","triggerScenarios":"422 from bad/missing date range or site_id; 404 when the stream route isn't deployed; 500 from the report agent; 502/504 from a proxy that times out the long-lived stream; a body-less HTTP/204 response (fails the !resp.body clause even though ok).","commonSituations":"Reverse proxy (nginx default 60s proxy_read_timeout) cutting off long report generation; site_id not matching the network inventory; env-based base URL (same candidate probing as error 18) resolving to a host without the stream route; CORS not exposing X-Agent-* headers (needs Access-Control-Expose-Headers).","solutions":["curl -N -i the endpoint with the exact payload to see status/body and whether the stream opens","If 502/504, raise proxy_read_timeout / proxy_buffering off for the stream route","If 422, validate start_date/end_date format and site_id against the backend schema before sending","Split the combined check: handle !resp.ok (HTTP error, read body) separately from !resp.body (no stream), and set Access-Control-Expose-Headers for X-Agent-*"],"exampleFix":"// before\nif (!resp.ok || !resp.body) {\n    const message = await resp.text()\n    throw new Error(`stream request failed: ${resp.status} ${message}`)\n}\n\n// after\nif (!resp.ok) {\n    const message = await resp.text().catch(() => '')\n    throw new Error(`stream request failed: ${resp.status} ${message.slice(0, 200)}`)\n}\nif (!resp.body) {\n    throw new Error('stream unavailable: response has no body')\n}","handlingStrategy":"validation","validationCode":"if (!question.trim()) throw new Error('question required'); if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(startDate) || !/^\\d{4}-\\d{2}-\\d{2}$/.test(endDate) || startDate > endDate) throw new Error('invalid date range');","typeGuard":"function isStreamOk(resp) { return resp.ok && !!resp.body && typeof resp.body.getReader === 'function'; }","tryCatchPattern":"try { const resp = await fetch(url, opts); if (!resp.ok) throw new Error(`stream request failed: ${resp.status}`); if (!resp.body) throw new Error('no stream body'); } catch (e) { fallbackToPollingReport(); }","preventionTips":["Validate date range and site_id before sending","Raise proxy read timeouts and disable buffering for stream routes","Expose X-Agent-* headers via Access-Control-Expose-Headers","Keep a polling fallback for environments where streaming fails"],"tags":["sse","streaming","http","proxy-timeout","cors-headers"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}