Issue #21642 has been updated by mame (Yusuke Endoh). I sympathize with this proposal. Having to rescue a wide variety of exceptions is a common pain point, especially when writing applications like chatbots. I believe the best approach is to classify exceptions based on the required user action, rather than by their underlying cause. The existing `IO::WaitReadable` and `IO::WaitWritable` modules are excellent precedents for this principle. They unify various underlying exceptions (e.g., `Errno::EAGAIN`, `Errno::EWOULDBLOCK`, `OpenSSL::SSLErrorWaitReadable`) under a single concept. In all these cases, the user's action is the same: wait for the IO object to become ready. This is a very rational approach. Applying this same logic to `ConnectionResetError` and `BrokenPipeError`, the user's response is almost always to treat the connection as unrecoverable and call `IO#close`. To parallel the naming convention of `IO::WaitReadable` (which is based on the action "wait"), a name like `IO::Close` or `IO::CloseUnrecoverable` seems like a consistent and descriptive choice, as it directly reflects the required action "close". While the exact name is certainly open for discussion, it follows this established pattern. Furthermore, I believe using this should be an includable module rather than part of a class hierarchy, mirroring the design of `IO::WaitReadable`. Changing existing code that raises `OpenSSL::SSL::SSLError` to raise a different exception class would be a major backward incompatibility. A much safer approach would be to have `OpenSSL::SSL::SSLError` simple `include` the `IO::CloseUnrecoverable` module. ---------------------------------------- Feature #21642: Introduce `IO::ConnectionResetError` and `IO::BrokenPipeError` as standardized IO-level exceptions. https://bugs.ruby-lang.org/issues/21642#change-114870 * Author: ioquatix (Samuel Williams) * Status: Open ---------------------------------------- Currently, different IO implementations in Ruby raise inconsistent exception types when a connection is reset or broken. For example: ```ruby # Plain TCP socket: socket.read_nonblock(1024) # => Errno::ECONNRESET # SSL socket: ssl_socket.read_nonblock(1024) # => OpenSSL::SSL::SSLError: SSL_read: unexpected eof while reading ``` Both represent a *connection reset by peer*, but the errors differ significantly in type and message. This inconsistency makes it difficult to handle connection-level errors generically across IO types. Similarly, `EPIPE` is used in some contexts to signal a *broken connection*, but again, the representation and message differ between IO classes. ### Proposal Introduce explicit subclasses of the corresponding system errors as part of Ruby’s standard IO interface: ```ruby class IO class ConnectionResetError < Errno::ECONNRESET; end class BrokenPipeError < Errno::EPIPE; end end ``` Then, standardize the Ruby I/O ecosystem (including OpenSSL) to raise these subclasses instead of raw system errors or library-specific error wrappers. This would establish a consistent, well-defined public interface for handling connection-level failures. ### Motivation * **Consistency:** Users can handle `IO::ConnectionResetError` across `IO`, `TCPSocket`, `OpenSSL::SSL::SSLSocket`, and other IO-like objects. * **Clarity:** The name clearly expresses a high-level semantic (“connection reset”) rather than a low-level system error. * **Extensibility:** Other Ruby IO implementations (custom sockets, pipes, etc.) can follow the same convention. * **Backwards Compatibility:** Because `IO::ConnectionResetError < Errno::ECONNRESET`, existing rescue clauses continue to work: ```ruby rescue Errno::ECONNRESET # still catches it end ``` ### Examples ```ruby begin io.read_nonblock(1024) rescue IO::ConnectionResetError puts "Connection was reset by peer." end ``` ### Impact on existing code * Minimal to none. * Existing code that rescues `Errno::ECONNRESET` or `Errno::EPIPE` will continue to function. * Future code gains a more semantic and portable way to handle these common failure modes. -- https://bugs.ruby-lang.org/