oven-sh/bun · error · TypeError
WriteFailed
WriteFailed
Error message
WriteFailed
What it means
Writing to the socket failed inside the HTTP client — the h2 session sets it when flushing the write buffer or sending frames hits an I/O error (src/http/h2_client/ClientSession.rs:782, :820, :917; src/http/lib.rs:1480). In practice the peer closed the connection while Bun was still writing (EPIPE/ECONNRESET), so the request fails mid-body. The same variant name is reused by other crates' enums for file-write failures, but in bun_http::Error it is specifically a socket/stream write failure.
Source
Thrown at src/http/error.rs:8
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("CompressionFailed")]
CompressionFailed,
#[error("Aborted")]
Aborted,
#[error("WriteFailed")]
WriteFailed,
#[error("HTTP2RefusedStream")]
HTTP2RefusedStream,
#[error("HTTP2ContentLengthMismatch")]
HTTP2ContentLengthMismatch,
#[error("HTTP2FrameSizeError")]
HTTP2FrameSizeError,
#[error("HTTP2ProtocolError")]
HTTP2ProtocolError,
#[error("HTTP2FlowControlError")]
HTTP2FlowControlError,
#[error("HTTP2EnhanceYourCalm")]
HTTP2EnhanceYourCalm,
#[error("HTTP2HeaderListTooLarge")]
HTTP2HeaderListTooLarge,
#[error("HTTP2StreamReset")]
HTTP2StreamReset,
#[error("HTTP2GoAway")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Retry the request — the connection is dead, but a fresh one usually succeeds; keep the body buffered so it is reusable.
- Send the request sooner after acquiring a pooled connection, or enable smaller keep-alive windows on your side so stale sockets are pruned.
- For long uploads, raise the server/proxy timeout (proxy_read_timeout, LB idle timeout) above your worst-case upload duration.
- If it happens consistently against one endpoint, capture `tcpdump`/server logs to see which side closes first — a server-side 408/499 preceding the close means the app is too slow, not the network.
Example fix
// before
const res = await fetch(url, { method: 'POST', body }); // fails once with WriteFailed after idle reuse
// after: retry once on write failure (idempotent request)
async function post(url, body) {
for (let i = 0; ; i++) {
try { return await fetch(url, { method: 'POST', body }); }
catch (e) { if (i >= 1 || !/WriteFailed|EPIPE|ECONNRESET/i.test(String(e.message))) throw e; }
}
} Defensive patterns
Strategy: retry
Type guard
function isWriteFailure(err) {
const s = String(err?.message ?? err);
return /WriteFailed|EPIPE|ECONNRESET|broken pipe/i.test(s);
} Try / catch
async function fetchRetryWrite(url, init, tries = 2) {
for (let i = 0; ; i++) {
try { return await fetch(url, init); }
catch (err) {
if (i >= tries || !isWriteFailure(err)) throw err;
await Bun.sleep(50 * (i + 1)); // new connection on retry
}
}
} Prevention
- Keep request bodies buffered (strings/ArrayBuffer) so retries can resend
- Only retry idempotent or safely repeatable requests after write failures
- Tune client keep-alive idle below the server's keepalive_timeout to avoid stale sockets
When it happens
Trigger: POSTing a request body to a server that already closed the connection (keep-alive race, server-side timeout, LB idle cutoff), writing to an h2 session after GOAWAY processing began, or a peer resetting the TCP connection mid-write.
Common situations: Keep-alive connections reused after the server's idle timeout (common with nginx keepalive_timeout shorter than the client pool's); proxies closing slow uploads; servers with aggressive request timeouts (e.g. 30s body limit) killing long uploads.
Related errors
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/f3942be82ac4ea18.
Report an issue: GitHub.