Skip to content

fix(client): swallow synchronous EPIPE from writeAfterFIN (#3282)#3283

Open
nkaradzhov wants to merge 2 commits into
redis:masterfrom
nkaradzhov:fix-3282-writeAfterFIN
Open

fix(client): swallow synchronous EPIPE from writeAfterFIN (#3282)#3283
nkaradzhov wants to merge 2 commits into
redis:masterfrom
nkaradzhov:fix-3282-writeAfterFIN

Conversation

@nkaradzhov
Copy link
Copy Markdown
Collaborator

@nkaradzhov nkaradzhov commented May 18, 2026

net.Socket.write can throw synchronously with code 'EPIPE' on a half-closed socket, before the 'close' event fires. The throw escaped RedisSocket.write from a setImmediate flush and crashed the process. Preflight writable, wrap the cork/write loop in try/finally so uncork always runs, and narrowly swallow EPIPE — the close handler will reject the pending command via #waitingForReply.

Fixes #3282

Description

Describe your pull request here


Checklist

  • Does npm test pass with this change (including linting)?
  • Is the new or changed code fully tested?
  • Is a documentation update included (if this change modifies existing APIs, or introduces new ones)?

Note

Medium Risk
Touches core socket write-path behavior; while narrowly scoped and covered by new tests, it could change how some edge-case disconnect/write failures are handled during reconnects.

Overview
Prevents process crashes when net.Socket.write throws synchronously on half-closed connections by skipping writes when the underlying socket is not writable, wrapping the corked write loop in try/finally to always uncork(), and swallowing only EPIPE while rethrowing other errors.

Adds targeted unit tests that capture the underlying net.Socket to verify the new short-circuit behavior, EPIPE swallowing, and non-EPIPE error propagation.

Reviewed by Cursor Bugbot for commit e9d7f00. Bugbot is set up for automated code reviews on this repo. Configure here.

net.Socket.write can throw synchronously with code 'EPIPE' on a
half-closed socket, before the 'close' event fires. The throw escaped
RedisSocket.write from a setImmediate flush and crashed the process.
Preflight `writable`, wrap the cork/write loop in try/finally so
uncork always runs, and narrowly swallow EPIPE — the close handler
will reject the pending command via #waitingForReply.

Fixes redis#3282

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace `any` casts with `unknown` and rename the unused `err`
parameter to `_err` so `lint:changed` passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

RedisSocket.write throws synchronously on writeAfterFIN, causing unhandled rejection (v5.12.1)

1 participant