{"record":{"id":"8f51a18385f60db1","repo":"datawhalechina/hello-agents","slug":"msg","errorCode":null,"errorMessage":"msg","messagePattern":"msg","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"Co-creation-projects/lcyting-StockSage-agent/frontend/src/views/StockAnalysis.vue","lineNumber":1050,"sourceCode":"      headers: {\n        'Content-Type': 'application/json',\n        Accept: 'application/x-ndjson',\n      },\n      body: JSON.stringify({\n        stock_code: stockCode.value.trim(),\n        stock_name: (stockName.value || '').trim(),\n      }),\n    })\n    if (!res.ok) {\n      const t = await res.text()\n      let msg = t || res.statusText\n      try {\n        const j = JSON.parse(t)\n        if (j.message) msg = j.message\n      } catch {\n        /* ignore */\n      }\n      throw new Error(msg)\n    }\n    const reader = res.body?.getReader()\n    if (!reader) throw new Error('无法读取响应流')\n\n    const dec = new TextDecoder()\n    let buf = ''\n    let streamError = null\n    let streamFinished = false\n\n    while (true) {\n      const { done: readerDone, value } = await reader.read()\n      if (value) buf += dec.decode(value, { stream: true })\n\n      let nl\n      while ((nl = buf.indexOf('\\n')) >= 0) {\n        const line = buf.slice(0, nl).trim()\n        buf = buf.slice(nl + 1)\n        if (!line) continue","sourceCodeStart":1032,"sourceCodeEnd":1068,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/lcyting-StockSage-agent/frontend/src/views/StockAnalysis.vue#L1032-L1068","documentation":"In StockSage's StockAnalysis.vue the code throws `new Error(msg)` when the analysis stream request responds non-OK. msg is resolved in priority order: JSON body's `message` field, else raw body text, else statusText — so the literal 'msg' in the error listing is just the variable name; the real message is whatever the server sent. This is the single rejection point for the Buffett-analysis streaming POST.","triggerScenarios":"POST to the stock-analysis stream endpoint with {stock_name, ...} returns non-2xx: 404 wrong URL, 422 empty/unknown stock_name (it is trimmed from an input), 500 when the LLM/stream pipeline fails, 502/503 when the gateway is down. The JSON.parse fallback exists because the backend sometimes returns an HTML/plain error page.","commonSituations":"stockName left blank after trim → backend validation error; backend not running or Vite dev proxy missing for the API path; upstream LLM API key invalid so the handler 500s with {message: ...}; HTML error pages from nginx breaking naive error rendering.","solutions":["Read the thrown message — when the backend sent {message: ...} it names the exact server-side failure; statusText-only means an empty body (check nginx/gateway).","Validate stockName is non-empty before invoking the stream (the code trims but the caller may skip checking).","Confirm the proxy config forwards the analysis endpoint to the backend port in dev and prod.","Check backend logs when the message references model/API errors (keys, quotas)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const name = (stockName.value || '').trim();\nif (!name) {\n  ElMessage.warning('请输入股票名称');\n  return;\n}","typeGuard":"function hasErrorMessageFrame(t: string): { message?: string } | null {\n  try {\n    const j = JSON.parse(t);\n    return typeof j === 'object' && j !== null && 'message' in j ? j : null;\n  } catch {\n    return null;\n  }\n}","tryCatchPattern":"try {\n  await runAnalysisStream(stockName.value.trim());\n} catch (err) {\n  const m = (err as Error).message;\n  if (/404|Not Found/i.test(m)) hintApiUrl();\n  else if (/422|validation/i.test(m)) hintStockName();\n  else showError(m || '分析请求失败');\n}","preventionTips":["Validate the stock name client-side before starting the stream.","Keep the existing JSON.message-first extraction; also log res.status for diagnosis.","Verify the dev proxy covers the analysis endpoint before shipping."],"tags":["http","sse","vue","streaming","error-envelope"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}