Describe the bug
When using remote form() functions, some users intermittently receive a 400 error:
Could not deserialize binary form: data too short
The error originates from @sveltejs/kit/src/runtime/form-utils.js in deserialize_binary_form, specifically line 246 where get_buffer() returns null because the request body stream ends before all expected bytes (based on Content-Length) have been read.
The issue is intermittent and user-dependent — it does not reproduce on localhost, and not all users are affected. This suggests a middlebox (reverse proxy / load balancer) is modifying the request body or its headers in transit.
This worked fine when hosted on Vercel, but started occurring after migrating to Azure App Service (Linux, adapter-node).
Switching from a remote form() to a traditional form action (+page.server.ts with use:enhance from $app/forms) resolves the issue entirely, since standard form submissions use application/x-www-form-urlencoded / multipart/form-data which middleboxes handle correctly.
Root cause analysis
The deserialize_binary_form function uses a custom binary protocol with content type application/x-sveltekit-formdata. It relies on the Content-Length header as the source of truth for validation:
const content_length = parseInt(request.headers.get('content-length') ?? '');
Azure App Service is known to modify request bodies in certain conditions — for example, decompressing Content-Encoding: gzip payloads without updating Content-Length (Azure/azure-functions-core-tools#2185), and stripping Content-Type headers from empty POST bodies (Azure/static-web-apps#1512).
When the actual body bytes don't match what Content-Length promises — because a proxy layer re-encoded, decompressed, or truncated the body — get_buffer() runs out of data and returns null, triggering the "data too short" error.
Possible solution
Rather than trusting Content-Length as the sole source of truth, deserialize_binary_form could:
- Read the actual stream length and validate against that instead of (or in addition to) the
Content-Length header.
- Validate based on the embedded header length fields — the binary format already contains
data_length (u32) and file_offsets_length (u16) in the first 7 bytes. These could be used to determine expected size independently of Content-Length.
- Consider using a standard content type (e.g.
multipart/form-data) as a fallback or default, since non-standard content types are more susceptible to middlebox interference.
Reproduction
Hard to reproduce locally. Occurs in production on Azure App Service (Linux) with adapter-node. Does not occur on Vercel.
Logs
System Info
System:
OS: Windows 11 10.0.26200
CPU: (16) x64 AMD Ryzen 7 5800H with Radeon Graphics
Memory: 6.19 GB / 31.36 GB
Binaries:
Node: 22.16.0 - C:\Program Files\nodejs\node.EXE
npm: 11.6.2 - C:\Program Files\nodejs\npm.CMD
pnpm: 8.5.0 - C:\Users\olive\AppData\Roaming\npm\pnpm.CMD
Deno: 1.28.3 - C:\Users\olive\.deno\bin\deno.EXE
Browsers:
Chrome: 144.0.7559.134
Edge: Chromium (141.0.3537.71)
Firefox: 107.0.1 - C:\Program Files\Mozilla Firefox\firefox.exe
Internet Explorer: 11.0.26100.7309
npmPackages:
@sveltejs/adapter-auto: ^7.0.0 => 7.0.0
@sveltejs/adapter-node: ^5.4.0 => 5.4.0
@sveltejs/adapter-vercel: ^5.6.3 => 5.6.3
@sveltejs/kit: ^2.50.2 => 2.50.2
@sveltejs/vite-plugin-svelte: ^6.2.3 => 6.2.3
svelte: ^5.49.1 => 5.49.1
vite: ^6.2.1 => 6.4.1
Severity
serious, but I can work around it
Additional Information
This is a production issue that affects end users intermittently, with no client-side workaround other than migrating away from remote form() functions to traditional form actions.
I got help from Claude Code to generate this issue, since I had no idea how to pinpoint the problem, so I hope the information is enough!
Describe the bug
When using remote
form()functions, some users intermittently receive a 400 error:The error originates from
@sveltejs/kit/src/runtime/form-utils.jsindeserialize_binary_form, specifically line 246 whereget_buffer()returnsnullbecause the request body stream ends before all expected bytes (based onContent-Length) have been read.The issue is intermittent and user-dependent — it does not reproduce on localhost, and not all users are affected. This suggests a middlebox (reverse proxy / load balancer) is modifying the request body or its headers in transit.
This worked fine when hosted on Vercel, but started occurring after migrating to Azure App Service (Linux, adapter-node).
Switching from a remote
form()to a traditional form action (+page.server.tswithuse:enhancefrom$app/forms) resolves the issue entirely, since standard form submissions useapplication/x-www-form-urlencoded/multipart/form-datawhich middleboxes handle correctly.Root cause analysis
The
deserialize_binary_formfunction uses a custom binary protocol with content typeapplication/x-sveltekit-formdata. It relies on theContent-Lengthheader as the source of truth for validation:Azure App Service is known to modify request bodies in certain conditions — for example, decompressing
Content-Encoding: gzippayloads without updatingContent-Length(Azure/azure-functions-core-tools#2185), and strippingContent-Typeheaders from empty POST bodies (Azure/static-web-apps#1512).When the actual body bytes don't match what
Content-Lengthpromises — because a proxy layer re-encoded, decompressed, or truncated the body —get_buffer()runs out of data and returnsnull, triggering the"data too short"error.Possible solution
Rather than trusting
Content-Lengthas the sole source of truth,deserialize_binary_formcould:Content-Lengthheader.data_length(u32) andfile_offsets_length(u16) in the first 7 bytes. These could be used to determine expected size independently ofContent-Length.multipart/form-data) as a fallback or default, since non-standard content types are more susceptible to middlebox interference.Reproduction
Hard to reproduce locally. Occurs in production on Azure App Service (Linux) with adapter-node. Does not occur on Vercel.
Logs
System Info
System: OS: Windows 11 10.0.26200 CPU: (16) x64 AMD Ryzen 7 5800H with Radeon Graphics Memory: 6.19 GB / 31.36 GB Binaries: Node: 22.16.0 - C:\Program Files\nodejs\node.EXE npm: 11.6.2 - C:\Program Files\nodejs\npm.CMD pnpm: 8.5.0 - C:\Users\olive\AppData\Roaming\npm\pnpm.CMD Deno: 1.28.3 - C:\Users\olive\.deno\bin\deno.EXE Browsers: Chrome: 144.0.7559.134 Edge: Chromium (141.0.3537.71) Firefox: 107.0.1 - C:\Program Files\Mozilla Firefox\firefox.exe Internet Explorer: 11.0.26100.7309 npmPackages: @sveltejs/adapter-auto: ^7.0.0 => 7.0.0 @sveltejs/adapter-node: ^5.4.0 => 5.4.0 @sveltejs/adapter-vercel: ^5.6.3 => 5.6.3 @sveltejs/kit: ^2.50.2 => 2.50.2 @sveltejs/vite-plugin-svelte: ^6.2.3 => 6.2.3 svelte: ^5.49.1 => 5.49.1 vite: ^6.2.1 => 6.4.1Severity
serious, but I can work around it
Additional Information
This is a production issue that affects end users intermittently, with no client-side workaround other than migrating away from remote form() functions to traditional form actions.
I got help from Claude Code to generate this issue, since I had no idea how to pinpoint the problem, so I hope the information is enough!