fix(frontend): apiFetchWithStatus body stream double-read crash #149

Merged
lekss361 merged 1 commit from fix/api-body-stream-double-read into main 2026-05-15 04:39:21 +00:00
lekss361 commented 2026-05-15 04:36:44 +00:00 (Migrated from github.com)

Bug

User report 2026-05-14: при analyze (cad search) с non-JSON error response (502 Caddy HTML / network failure) frontend крашится:

Failed to execute 'text' on 'Response': body stream already read

Root cause

apiFetchWithStatus в lib/api.ts:49-58:

  1. await response.json() consumes body stream даже если parse fails
  2. catch → await response.text() FAILS — stream consumed

fetch API: response body — ReadableStream, consumable один раз.

Fix

Read text() once → JSON.parse(string). На fail оборачиваем в {detail: rawText}.

Impact

Cad search не падает на error pages — пользователь видит реальный backend detail.

## Bug User report 2026-05-14: при analyze (cad search) с non-JSON error response (502 Caddy HTML / network failure) frontend крашится: > `Failed to execute 'text' on 'Response': body stream already read` ## Root cause `apiFetchWithStatus` в `lib/api.ts:49-58`: 1. `await response.json()` consumes body stream даже если parse fails 2. catch → `await response.text()` FAILS — stream consumed fetch API: response body — ReadableStream, consumable один раз. ## Fix Read text() once → `JSON.parse(string)`. На fail оборачиваем в `{detail: rawText}`. ## Impact Cad search не падает на error pages — пользователь видит реальный backend detail.
lekss361 commented 2026-05-15 04:39:01 +00:00 (Migrated from github.com)

Bot review (SHA 449be21, CI )

Корректный fix классического fetch-API gotcha.

Root cause верно идентифицирован: fetch response.body — ReadableStream, consumable один раз. await response.json() его consume'ит даже при parse-fail; последующий await response.text() крашится "body stream already read".

Fix pattern стандартный — read raw text once → JSON.parse(rawText) → fallback wrap в {detail: rawText}. Это идиоматично.

if (rawText) guard — пустое тело (200 без content, edge case) корректно остаётся body = null, не превращается в {detail: ""} с ложным detail-полем.

response.status !== 204 skip preserved — backward compatible с предыдущим контрактом.

Tiny surgical diff (+10/-6), CI green.

approve merge

## Bot review (SHA `449be21`, CI ✅) Корректный fix классического fetch-API gotcha. ✅ **Root cause верно идентифицирован**: fetch `response.body` — ReadableStream, consumable один раз. `await response.json()` его consume'ит даже при parse-fail; последующий `await response.text()` крашится `"body stream already read"`. ✅ **Fix pattern стандартный** — read raw text once → `JSON.parse(rawText)` → fallback wrap в `{detail: rawText}`. Это идиоматично. ✅ **`if (rawText)` guard** — пустое тело (200 без content, edge case) корректно остаётся `body = null`, не превращается в `{detail: ""}` с ложным detail-полем. ✅ **`response.status !== 204` skip preserved** — backward compatible с предыдущим контрактом. ✅ Tiny surgical diff (+10/-6), CI green. approve merge
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#149
No description provided.