datawhalechina/hello-agents · error · Error

无法读取响应流

Error message

无法读取响应流

What it means

Thrown in StockAnalysis.vue when res.ok is true but res.body is null/undefined, so getReader() cannot be created — '无法读取响应流' (cannot read response stream). It guards the NDJSON-line streaming loop that follows; without a reader there is no way to consume the incremental analysis deltas. Unlike the !res.ok branch, this fires on a 200 response whose body was stripped or never provided.

Source

Thrown at Co-creation-projects/lcyting-StockSage-agent/frontend/src/views/StockAnalysis.vue:1053

      },
      body: JSON.stringify({
        stock_code: stockCode.value.trim(),
        stock_name: (stockName.value || '').trim(),
      }),
    })
    if (!res.ok) {
      const t = await res.text()
      let msg = t || res.statusText
      try {
        const j = JSON.parse(t)
        if (j.message) msg = j.message
      } catch {
        /* ignore */
      }
      throw new Error(msg)
    }
    const reader = res.body?.getReader()
    if (!reader) throw new Error('无法读取响应流')

    const dec = new TextDecoder()
    let buf = ''
    let streamError = null
    let streamFinished = false

    while (true) {
      const { done: readerDone, value } = await reader.read()
      if (value) buf += dec.decode(value, { stream: true })

      let nl
      while ((nl = buf.indexOf('\n')) >= 0) {
        const line = buf.slice(0, nl).trim()
        buf = buf.slice(nl + 1)
        if (!line) continue
        let obj
        try {
          obj = JSON.parse(line)

View on GitHub (pinned to 606a07d341)

Solutions

  1. Log res.type — 'opaque' means a CORS/no-cors problem; fix the CORS headers or remove mode:'no-cors' from the fetch.
  2. Ensure the streaming route disables proxy buffering (nginx: proxy_buffering off; or X-Accel-Buffering: no response header).
  3. Verify the backend always returns a streaming body for this route, including on its 'immediate reply' path.
  4. Test in a modern browser to rule out missing resp.body support.
Defensive patterns

Strategy: type-guard

Validate before calling

const streamCapable = typeof ReadableStream !== 'undefined' && 'body' in Response.prototype;
if (!streamCapable) {
  throw new Error('当前环境不支持流式响应');
}
// ensure fetch is same-origin/cors, never mode:'no-cors'
const resp = await fetch(url, { method: 'POST', headers, body }); // no mode:'no-cors' anywhere

Type guard

function hasReadableBody(res: Response): res is Response & { body: NonNullable<Response['body']> } {
  return res.body != null;
}

Try / catch

const reader = hasReadableBody(res) ? res.body.getReader() : null;
if (!reader) {
  if (res.type === 'opaque') showError('CORS 配置错误导致无法读取响应流');
  else showError('当前浏览器不支持流式响应');
  return;
}

Prevention

When it happens

Trigger: The stream endpoint returns 200 with no body: proxy or middleware that buffers and closes the stream, a backend handler that returns Response without a body on some path, an environment where fetch response streaming is unavailable (resp.body undefined in old browsers/WebViews), or an opaque response mode (mode:'opaque' from no-cors) which always has null body.

Common situations: CORS misconfiguration making the fetch fall into an opaque response; nginx buffering config for the streaming route; old in-app browsers lacking ReadableStream responses.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/6f4a8444a292bfa9. Report an issue: GitHub.