{"record":{"id":"202281a1ebc1b2e0","repo":"continuedev/continue","slug":"non-200-response-body-await-response-text","errorCode":null,"errorMessage":"non-200 response body: ${await response.text()}","messagePattern":"non-200 response body: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/fetch/src/stream.ts","lineNumber":18,"sourceCode":"export async function* toAsyncIterable(\n  nodeReadable: NodeJS.ReadableStream,\n): AsyncGenerator<Uint8Array> {\n  for await (const chunk of nodeReadable) {\n    // @ts-ignore\n    yield chunk as Uint8Array;\n  }\n}\n\nexport async function* streamResponse(\n  response: Response,\n): AsyncGenerator<string> {\n  if (response.status === 499) {\n    return; // In case of client-side cancellation, just return\n  }\n\n  if (response.status !== 200) {\n    throw new Error(await response.text());\n  }\n\n  if (!response.body) {\n    throw new Error(\"No response body returned.\");\n  }\n\n  // Get the major version of Node.js\n  const nodeMajorVersion = parseInt(process.versions.node.split(\".\")[0], 10);\n  let chunks = 0;\n\n  try {\n    if (nodeMajorVersion >= 20) {\n      // Use the new API for Node 20 and above\n      const stream = (ReadableStream as any).from(response.body);\n      for await (const chunk of stream.pipeThrough(\n        new TextDecoderStream(\"utf-8\"),\n      )) {\n        yield chunk;","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/continuedev/continue/blob/5522c6f44ca0ac3528b37244818fbfa39b5af470/packages/fetch/src/stream.ts#L1-L36","documentation":"streamResponse throws the response body text when the HTTP status is not 200 (and not 499, which signals client cancellation and returns silently). The body text typically carries the server's error message.","triggerScenarios":"Any streamed request (streamSse/streamJSON) whose response has status 400/401/403/404/429/500 etc.; the awaited response.text() becomes the error message.","commonSituations":"Expired or wrong API keys (401), hitting rate limits (429), wrong endpoint paths (404), or upstream server errors (500) during chat completion streaming.","solutions":["Inspect status before streaming: if (response.status !== 200) handle error with status and body","Fix auth/endpoint issues indicated by the status code (401 → API key, 429 → backoff, 404 → URL)","Retry with exponential backoff for 429/5xx","Log response.headers like 'x-ratelimit-*' to diagnose limits"],"exampleFix":"// before\nconst stream = streamSse(response);\n\n// after\nif (response.status !== 200) {\n  throw new Error(`HTTP ${response.status}: ${await response.text()}`);\n}\nconst stream = streamSse(response);","handlingStrategy":"try-catch","validationCode":"if (response.status !== 200) { const text = await response.text(); throw Object.assign(new Error(text), { status: response.status }); }","typeGuard":null,"tryCatchPattern":"try { for await (const c of streamSse(response)) {} } catch (e) { if (e.message.includes('status')) {} /* handle */ }","preventionTips":["Check response.status before passing to streamSse/streamJSON","Centralize status→error mapping with retry/backoff for 429/5xx","Refresh auth tokens on 401"],"tags":["fetch","streaming","http-status","network"],"backgroundTag":"http-error-response","analyzedSha":"5522c6f44ca0ac3528b37244818fbfa39b5af470","analyzedAt":"2026-08-27T11:28:54.683Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}