{"record":{"id":"881cabb609145baf","repo":"yikart/AiToEarn","slug":"error-881cab","errorCode":null,"errorMessage":"无法获取响应流","messagePattern":"无法获取响应流","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-electron/src/views/task/task.tsx","lineNumber":1041,"sourceCode":"        import.meta.env.VITE_APP_URL + '/tools/ai/article/html/sse',\n        {\n          method: 'POST',\n          headers: {\n            'Content-Type': 'application/json',\n          },\n          body: JSON.stringify({\n            content: '生成一个卡通人物介绍页 带有图片 小红书图文流光卡片样式',\n          }),\n        },\n      );\n\n      if (!response.ok) {\n        throw new Error(`HTTP error! status: ${response.status}`);\n      }\n\n      const reader = response.body?.getReader();\n      if (!reader) {\n        throw new Error('无法获取响应流');\n      }\n\n      let htmlString = '';\n      let isCollectingHtml = false;\n      let buffer = '';\n\n      // 处理响应流\n      while (true) {\n        const { done, value } = await reader.read();\n        if (done) {\n          console.log('流读取完成');\n          setHtmlContent(htmlString); // 设置最终的 HTML 内容\n          break;\n        }\n\n        // 将 Uint8Array 转换为文本\n        const text = new TextDecoder().decode(value);\n        buffer += text;","sourceCodeStart":1023,"sourceCodeEnd":1059,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/src/views/task/task.tsx#L1023-L1059","documentation":"After the fetch succeeds in testSseFunc, it reads the SSE stream via response.body.getReader(). If response.body is null (no readable stream), it throws '无法获取响应流'. Per fetch spec the body can be null for certain responses (opaque no-cors responses, 204/304, or non-browser contexts lacking stream support).","triggerScenarios":"Calling testSseFunc with mode:'no-cors' (opaque response ⇒ body null), the server returning 204/304, or a runtime where ReadableStream response bodies are unavailable.","commonSituations":"Copied the fetch pattern from a CORS-enabled context into a no-cors request; a proxy/CDN strips the body; older Electron/Chromium build with streaming disabled for the response type.","solutions":["Ensure the request is CORS-enabled (no opaque 'no-cors' mode) so response.body is a real stream","Check response.status — 204/304 have no body; treat them separately","Verify the server sends chunked/streaming (SSE) responses and no proxy buffers/strips them","Fall back to non-streaming consumption (response.text()) when body is unavailable"],"exampleFix":"// before\nconst reader = response.body?.getReader();\nif (!reader) {\n  throw new Error('无法获取响应流');\n}\n// after\nif (!response.body) {\n  const text = await response.text(); // fallback: consume whole body\n  handleNonStreamed(text);\n  return;\n}\nconst reader = response.body.getReader();","handlingStrategy":"type-guard","validationCode":"const res = await fetch(url, init)\nif (!res.ok) throw new Error(`HTTP ${res.status}`)\nif (!res.body) throw new Error('no stream body') // check before streaming","typeGuard":"function hasReadableBody(r: Response): r is Response & { body: ReadableStream } {\n  return r.body !== null\n}","tryCatchPattern":"try {\n  await testSseFunc()\n} catch (e) {\n  if (e.message === '无法获取响应流') {\n    return fallbackToNonStreamingFetch(url, init) // e.g. response.text()\n  }\n  throw e\n}","preventionTips":["Never use mode:'no-cors' for SSE — the opaque response has a null body","Handle 204/304 responses explicitly (no body by spec)","Confirm the server sends Content-Type: text/event-stream and proxies don't buffer it"],"tags":["sse","streaming","fetch","electron"],"backgroundTag":"response-body-null","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}