Skip to content

Remote form binary deserialization fails intermittently on Azure App Service ("data too short") #15299

@oliie

Description

@oliie

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:

  1. Read the actual stream length and validate against that instead of (or in addition to) the Content-Length header.
  2. 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.
  3. 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!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions