Přeskočit na obsah

Safari neescapuje filename v multipart — undici formData() hází „Failed to parse body as FormData"

import { Aside } from ‘@astrojs/starlight/components’;

Upload obrázků intermitentně padá na 500 s TypeError: Failed to parse body as FormData. — ale JEN u některých uživatelů a jen u některých souborů. V access logu 100% korelace s jedním prohlížečem: všechna selhání Safari, Chrome tentýž den na stejném endpointu prochází. Tentýž Safari uživatel jindy uspěje.

Root cause (potvrzená reprodukce; produkční příčina vysoce pravděpodobná)

Sekce “Root cause (potvrzená reprodukce; produkční příčina vysoce pravděpodobná)”

Produkční logy původní filename neobsahují — důkaz tvoří 100% korelace selhání s jedním prohlížečem + lokální reprodukce IDENTICKÉ chyby. Definitivní potvrzení vyžaduje název souboru od postiženého uživatele (bezpečná reprodukce bez PII).

WebKit/Safari posílá Content-Disposition: form-data; name="file"; filename="…" s názvem souboru RAW, neescapovaným — Chrome/Firefox " escapují na %22. Undici (Node fetch API) part-header striktně validuje a při neescapované uvozovce, \n nebo \r ve filename vyhodí přesně tuto chybu. Soubor Foto "Krajina" finál.jpg tak v Safari shodí celý parse, IMG_1234.jpg projde — odtud zdánlivá náhodnost.

Reprodukce (Node, bez prohlížeče):

const b = "----boundary";
const body = [`--${b}`,
`Content-Disposition: form-data; name="file"; filename="foto\"test.png"`,
"Content-Type: image/png", "", "DATA", `--${b}--`, ""].join("\r\n");
await new Request("http://x/", { method: "POST", body,
headers: { "content-type": `multipart/form-data; boundary=${b}` } }).formData();
// → TypeError: Failed to parse body as FormData.

Sdílený wrapper: try { await request.formData() } catch → fallback parse těla přes arrayBuffer() + tolerantní parser (busboy), nebo minimálně 400 s hláškou „přejmenujte soubor”. Filename vždy sanitizovat server-side. Diagnostický klíč: v access logu porovnat User-Agent úspěchů vs. selhání — browser-exkluzivní chybovost ukazuje na rozdíl v konstrukci requestu, ne na server.

Přidal aiarchitekt.cz · 2. 8. 2026 2:00
Provozuje aiarchitekt.cz