From e109400fcdc4b65128ca47f1272c736b760c4a4c Mon Sep 17 00:00:00 2001 From: lekss361 Date: Fri, 15 May 2026 07:34:37 +0300 Subject: [PATCH] fix(frontend): apiFetchWithStatus body stream double-read crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per user report: 'Failed to execute text on Response: body stream already read' при analyze запросе с error response. ## Root cause apiFetchWithStatus делал: 1. await response.json() (consumes body stream даже на parse error) 2. catch → await response.text() ← FAILS — stream already consumed fetch API: response body — ReadableStream, consumable один раз. ## Fix Read text() once → attempt JSON.parse. Если parse failed (HTML error page от Caddy / 502 / etc) — оборачиваем в {detail: rawText}. Никаких double-reads. ## Impact Любой non-JSON error response (502/504/HTML/etc) теперь показывает реальный detail вместо misleading 'body stream' error. Refs: user report 2026-05-14 --- frontend/src/lib/api.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index ff1c5b02..64559e2c 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -47,13 +47,17 @@ export async function apiFetchWithStatus( }, }); let body: unknown = null; - // 204 No Content имеет пустое тело; для всех остальных пытаемся JSON + // 204 No Content имеет пустое тело; для всех остальных читаем как text один раз + // (fetch body stream consumable только один раз — нельзя сначала .json() потом .text()). if (response.status !== 204) { - try { - body = await response.json(); - } catch { - // fallback на text() — может прилететь HTML error page от Caddy - body = { detail: await response.text() }; + const rawText = await response.text(); + if (rawText) { + try { + body = JSON.parse(rawText); + } catch { + // Не JSON (HTML error page от Caddy / 502 / etc) — оборачиваем в detail + body = { detail: rawText }; + } } } if (!response.ok) {