{"record":{"id":"00d2819ebdcde208","repo":"chatboxai/chatbox","slug":"no-response-body","errorCode":null,"errorMessage":"No response body","messagePattern":"No response body","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/routes/guide/-hooks/useGuideSession.ts","lineNumber":702,"sourceCode":"        // Prepare messages for API\n        const apiMessages: GuideMessage[] = messages\n          .filter((m) => m.role === 'user' || m.role === 'assistant')\n          .map((m) => ({\n            role: m.role,\n            content: m.content,\n          }))\n        apiMessages.push({ role: 'user', content })\n\n        // Send request with current onboarding step\n        const { uuid: deviceId } = await platform.getConfig()\n        const response = await sendGuideMessage(apiMessages, deviceId, {\n          onboardingStep,\n          isLoggedIn,\n          signal: abortControllerRef.current.signal,\n        })\n\n        if (!response.body) {\n          throw new Error('No response body')\n        }\n\n        // Parse streaming response\n        await parseStreamResponse(response.body.getReader(), {\n          setMessages,\n          setOnboardingStep,\n          pendingUpdateRef,\n          pendingTimeouts: pendingTimeoutsRef.current,\n          markGuideCompleted,\n          t,\n        })\n      } catch (err) {\n        if ((err as Error).name === 'AbortError') {\n          // User cancelled, just mark streaming as done\n          setMessages((prev) => {\n            const lastIdx = prev.length - 1\n            if (prev[lastIdx]?.isStreaming) {\n              return [","sourceCodeStart":684,"sourceCodeEnd":720,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/renderer/routes/guide/-hooks/useGuideSession.ts#L684-L720","documentation":"Thrown by the onboarding-guide chat loop after sendGuideMessage resolves. The guide API is a streaming POST (stream: true, retry: 0) that must return a readable Response.body; if the fetch resolved with a bodyless response the stream parser cannot run. The guard exists because a Response object can exist (status received) while body is null — e.g. opaque/error responses or empty 204s.","triggerScenarios":"sendGuideMessage returns a Response whose .body is null. Causes: the server replies 204 No Content or a non-streaming error with empty body, a network/CORS issue produces an opaque response (body null), a proxy returns headers then closes without a body, or the request was redirected. Because retry:0, transient transport errors surface here instead of being retried.","commonSituations":"Guide backend deployed without streaming support, reverse proxy buffering and stripping the body, CORS misconfiguration returning an opaque response, ad-blocker/privacy extension blocking the guide endpoint, or a 5xx where the server sent no body.","solutions":["Inspect response.status and response.headers before throwing to give the user a meaningful error (e.g. 5xx vs CORS).","Verify the guide API endpoint is reachable and returns text/event-stream with a non-empty body.","Confirm CORS headers allow the origin and that no proxy buffers the stream.","Retry on transient status codes (the retry:0 was set to avoid double-billing; consider a single retry only when status indicates no server-side processing started)."],"exampleFix":"// before\nif (!response.body) {\n  throw new Error('No response body')\n}\n// after\nif (!response.body) {\n  if (response.status === 0 || response.type === 'opaque') throw new Error('Guide request blocked (CORS/network)')\n  throw new Error(`No response body (HTTP ${response.status})`)\n}","handlingStrategy":"try-catch","validationCode":"const response = await sendGuideMessage(apiMessages, deviceId, { onboardingStep, isLoggedIn, signal })\nif (!response.ok || response.status === 0) {\n  throw new Error(`Guide request failed before streaming (HTTP ${response.status})`)\n}","typeGuard":"function hasReadableBody(r: Response): r is Response & { body: ReadableStream<Uint8Array> } {\n  return r.body != null && typeof r.body.getReader === 'function'\n}","tryCatchPattern":"try {\n  const response = await sendGuideMessage(...)\n  if (!hasReadableBody(response)) {\n    if (response.type === 'opaque' || response.status === 0) setError('Request blocked (network/CORS)')\n    else setError(`No response body (HTTP ${response.status})`)\n    return\n  }\n  await parseStreamResponse(response.body.getReader(), ...)\n} catch (err) {\n  if ((err as Error).name === 'AbortError') return\n  setError((err as Error).message)\n}","preventionTips":["Check response.ok and response.type before reading the body to give a precise error.","Ensure CORS headers on the guide endpoint allow the app origin.","Keep retry:0 but add explicit status-based messaging for empty responses."],"tags":["network","streaming","guide-api","cors","fetch"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}