fix: JSON-encode object fields in multipart request bodies (#337)
`ApiClient` built its multipart body with a bare `FormData.append(key, value)`, so any non-file field was coerced by `String()`. A `File` survives that, an object does not. `StreamClient.uploadFile`/`uploadImage` worked around it by hand-stringifying `user` and `upload_sizes` behind `@ts-expect-error`. The channel-scoped uploads — `chat.uploadChannelFile`, `chat.uploadChannelImage`, `channel.uploadChannelFile`, `channel.uploadChannelImage` — had no such wrapper and were broken outright. Verified against the API: ``` Error: Stream error code 4: UploadChannelFile failed with error: "bad user id" ``` That's the server reading `"[object Object]"` as the user id. Encoding now lives in `ApiClient.multipartBodyStringify`: files pass through as `Blob`s, scalars keep their existing coercion, objects and arrays get `JSON.stringify`, and no-value fields are dropped instead of being sent as the string `"undefined"` (which the patched global path was also doing). With one encoding site the `StreamClient` workarounds are redundant and were removed; the overrides stay because the spec still types `file` as `string`. ## Testing New `__tests__/multipart.test.ts` asserts on the `FormData` that actually reaches `fetch` — object/array/file/scalar/no-value fields, no double-encoding of pre-encoded strings, plus all four upload entry points. Runs in CI without credentials. Each new-behavior test was confirmed to fail before the fix. Also added a channel-upload suite to `__tests__/file-uploads.test.ts`, which had no coverage — this is why the bug shipped. Both suites in that file stay `describe.skip` per the existing convention there; run live, all 4 pass with the fix and the 2 channel tests fail without it.
O
Oliver Lazoroski committed
5ed3f5b6ef627fdb1497bb6fbf669db213c31b0e
Parent: cd55e36
Committed by GitHub <noreply@github.com>
on 8/21/2026, 2:59:46 PM