Think of it this way.
When your listener gets an onClose() event, then:
1) remote has initiated the close handshake on the connection by sending a CLOSE frame. (Jetty's implementation will automatically respond to the handshake when the current message is completed sending)
2) *or* the local endpoint has determined that the connection is closed without a handshake, and is telling you via an abnormal closure code, such as 1006.
In the case where you get an onError(), all bets are off, the connection is now suspect, and the connection is likely being shut down.
WebSocket being a framed protocol, the ability to recover is highly dependent on the state of the protocol at the time of the exception.
If we had an API that used raw frames instead of messages, then this kind of recovery might have been possible by the application (but then the application would have to intimately understand the WebSocket protocol state in order to make that determination)
Also, since the JSR356 spec cannot communicate if an onError() is recoverable or not, we decided to make the Jetty side implementation the same.