What version of Effect is running?
effect@4.0.0-rc.115
@effect/platform-bun@4.0.0-rc.115
- Bun
1.4.2, Linux x64 (also reproduced on Bun 1.3.13)
What steps can reproduce the bug?
Create an empty directory with this package.json:
{
"private": true,
"type": "module",
"dependencies": {
"effect": "4.0.0-rc.115",
"@effect/platform-bun": "4.0.0-rc.115"
}
}
Save the following as repro.mjs. The request supplies a valid multipart file header and five file bytes, then its body stream errors while that file is active. highWaterMark: 0 keeps the error on the next pull rather than prefetching it before the buffered data is consumed.
import { Effect, Stream } from "effect";
import * as BunMultipart from "@effect/platform-bun/BunMultipart";
const body = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode(
'--b\r\nContent-Disposition: form-data; name="file"; filename="a.txt"\r\n\r\nhello',
));
},
pull(controller) {
controller.error(new Error("body-read-failed"));
},
}, { highWaterMark: 0 });
const request = new Request("http://localhost/upload", {
method: "POST",
headers: { "content-type": "multipart/form-data; boundary=b" },
body,
});
let bytesRead = 0;
const result = await Effect.runPromiseExit(
BunMultipart.stream(request).pipe(
Stream.runForEach((part) => part._tag === "File"
? Stream.runForEach(part.content, (chunk) => Effect.sync(() => { bytesRead += chunk.length; }))
: Effect.void),
Effect.timeout("1 second"),
),
);
console.log({ bun: Bun.version, bytesRead });
console.log(JSON.stringify(result, null, 2));
Run:
bun install --ignore-scripts
bun --no-install repro.mjs
What is the expected behavior?
Consuming the active file should promptly fail with the request-body read error, wrapped in MultipartError. The outer multipart-consumption effect should fail without reaching the watchdog timeout.
What do you see instead?
Five file bytes are consumed, then the operation fails only when the outer timeout expires:
{ bun: "1.4.2", bytesRead: 5 }
The resulting failure is TimeoutError, with the message Operation timed out after '1s', rather than the injected body-read-failed error.
As a control, consuming an identically constructed body through BunStream.fromReadableStream propagates the read error without timing out.
Additional information
In Multipart.makeChannel, the file-content pull loop checks buffered chunks and finished. pump records an upstream failure in exit, but the nested file loop does not check that value before pumping again. The outer part loop checks it, but the consumer is still awaiting the active file.
Related history: #7155 proposed propagating upstream failures to active files and was closed in favor of #7156. The latter addresses parser limits/truncated input. This reproduction uses an explicit request-body read failure with no configured parser limits and fails before any closing boundary is received. #6284 is a related limit-triggered hang report.
What version of Effect is running?
effect@4.0.0-rc.115@effect/platform-bun@4.0.0-rc.1151.4.2, Linux x64 (also reproduced on Bun1.3.13)What steps can reproduce the bug?
Create an empty directory with this
package.json:{ "private": true, "type": "module", "dependencies": { "effect": "4.0.0-rc.115", "@effect/platform-bun": "4.0.0-rc.115" } }Save the following as
repro.mjs. The request supplies a valid multipart file header and five file bytes, then its body stream errors while that file is active.highWaterMark: 0keeps the error on the next pull rather than prefetching it before the buffered data is consumed.Run:
What is the expected behavior?
Consuming the active file should promptly fail with the request-body read error, wrapped in
MultipartError. The outer multipart-consumption effect should fail without reaching the watchdog timeout.What do you see instead?
Five file bytes are consumed, then the operation fails only when the outer timeout expires:
The resulting failure is
TimeoutError, with the messageOperation timed out after '1s', rather than the injectedbody-read-failederror.As a control, consuming an identically constructed body through
BunStream.fromReadableStreampropagates the read error without timing out.Additional information
In
Multipart.makeChannel, the file-content pull loop checks buffered chunks andfinished.pumprecords an upstream failure inexit, but the nested file loop does not check that value before pumping again. The outer part loop checks it, but the consumer is still awaiting the active file.Related history: #7155 proposed propagating upstream failures to active files and was closed in favor of #7156. The latter addresses parser limits/truncated input. This reproduction uses an explicit request-body read failure with no configured parser limits and fails before any closing boundary is received. #6284 is a related limit-triggered hang report.