From df1bbef14303c2050bb04ca0d539a630b37d9266 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Mon, 4 May 2026 13:55:57 +0200 Subject: [PATCH] feat(#251): expose backend error detail in apiFetch errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le wrapper apiFetch jetait `API error: 422` sans le body. Désormais lit detail (string ou liste Pydantic) et le concatène au message : `422: Neo4j config requires a non-empty 'index_name'`. Permet aux pages StoreCreatePage / StoreEditPage de surfacer la cause réelle du refus serveur dans l'errorMessage du form. --- frontend/src/shared/api/http.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/frontend/src/shared/api/http.ts b/frontend/src/shared/api/http.ts index 5d2bf02..806a394 100644 --- a/frontend/src/shared/api/http.ts +++ b/frontend/src/shared/api/http.ts @@ -13,7 +13,31 @@ export async function apiFetch(url: string, options: FetchOptions = ...options, headers, }) - if (!response.ok) throw new Error(`API error: ${response.status}`) + if (!response.ok) { + const detail = await readErrorDetail(response) + throw new Error(detail ? `${response.status}: ${detail}` : `API error: ${response.status}`) + } if (response.status === 204) return null as T return response.json() as Promise } + +async function readErrorDetail(response: Response): Promise { + try { + const contentType = response.headers.get('content-type') ?? '' + if (!contentType.includes('application/json')) return null + const body = await response.json() + if (typeof body?.detail === 'string') return body.detail + if (Array.isArray(body?.detail)) { + return body.detail + .map((e: { loc?: unknown[]; msg?: string }) => { + const loc = Array.isArray(e.loc) ? e.loc.join('.') : '' + return loc ? `${loc}: ${e.msg ?? ''}` : (e.msg ?? '') + }) + .filter(Boolean) + .join('; ') + } + return null + } catch { + return null + } +}