jsonrpc2: wrap write error in server-closing error (#1186)

The "server is closing" error produced when a connection shuts down
because its write side failed formatted the underlying write error with
`%v`, so the cause - typically `io.EOF` when a stdio host closes its
pipe - was not part of the error chain. A caller that wants to
distinguish a clean host disconnect from a real failure via
`errors.Is(err, io.EOF)` therefore always got `false`.

This changes both `s.writeErr` wrappings to use `%w`, preserving the
write error in the error chain while keeping the same message text
("server is closing: EOF").

A regression test exercises the write-failure branch of `shuttingDown`
and asserts both `errors.Is(err, ErrServerClosing)` and `errors.Is(err,
io.EOF)`.

Fixes #1098

---------

Co-authored-by: Bo Zhang <187063395+Tethys0@users.noreply.github.com>
Co-authored-by: Guglielmo Colombo <guglielmoc@google.com>
This commit is contained in:
Bobo 2026-08-27 09:12:53 -05:00 • committed by GitHub
commit 165121e53e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -158,12 +158,12 @@ func (s *inFlightState) shuttingDown(errClosing error) error {
if s.readErr != nil {
// If the read side of the connection is broken, we cannot read new call
// requests, and cannot read responses to our outgoing calls.
return fmt.Errorf("%w: %v", errClosing, s.readErr)
return fmt.Errorf("%w: %w", errClosing, s.readErr)
}
if s.writeErr != nil {
// If the write side of the connection is broken, we cannot write responses
// for incoming calls, and cannot write requests for outgoing calls.
return fmt.Errorf("%w: %v", errClosing, s.writeErr)
return fmt.Errorf("%w: %w", errClosing, s.writeErr)
}
return nil
}