Skip to content

fix(functions): pass ReadableStream body through without JSON serialization#2469

Open
i-anubhav-anand wants to merge 1 commit into
supabase:masterfrom
i-anubhav-anand:fix/functions-readablestream-body
Open

fix(functions): pass ReadableStream body through without JSON serialization#2469
i-anubhav-anand wants to merge 1 commit into
supabase:masterfrom
i-anubhav-anand:fix/functions-readablestream-body

Conversation

@i-anubhav-anand

Copy link
Copy Markdown
Contributor

🔍 Description

What changed?

FunctionInvokeOptions.body documents ReadableStream<Uint8Array> as a supported type, but the body-inference logic in invoke() had no branch for it. A ReadableStream matched none of the Blob/ArrayBuffer/string/FormData cases and fell through to JSON.stringify(functionArgs) — which produces "{}", silently dropping the entire stream payload and mislabeling it application/json. This happened in both body-inference paths (with and without a caller-supplied Content-Type).

This PR:

  1. Adds a ReadableStream branch in both paths that forwards the stream untouched (no forced application/json).
  2. Sets duplex: 'half' on the fetch call when the body is a ReadableStream, which native (undici) fetch requires for a streaming request body — mirroring the existing storage-js behavior added in fix(storage): forward duplex option for stream uploads via uploadToSignedUrl #2287.

Why was this change needed?

A documented, supported body type was silently corrupted into "{}". Per the WHATWG Fetch standard, ReadableStream is a valid BodyInit, and the library's own types.ts lists it as supported — so serializing it as JSON is a contract violation.

🔄 Breaking changes

  • This PR contains no breaking changes

📋 Checklist

📝 Additional notes

Fail-before / pass-after — added test/spec/readablestream-body.spec.ts (Docker-free, custom-fetch capture) covering a stream body both with and without a caller Content-Type, asserting the stream is forwarded unchanged and duplex: 'half' is set:

  • Before fix (on master): ❌ both cases — expect(received).toBe(stream)Received: "{}"
  • After fix: ✅ both pass (stream forwarded, duplex: 'half' set)

@i-anubhav-anand
i-anubhav-anand requested review from a team as code owners June 23, 2026 15:47
Comment on lines +295 to +298
// a web ReadableStream body requires the half-duplex flag for native (undici) fetch
...(typeof ReadableStream !== 'undefined' && body instanceof ReadableStream
? { duplex: 'half' }
: {}),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of ternary matching here with the Readable guard again, could you move this into the Readable branch, defined at ln 244.

This can be done by following the same approach that headers does

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants