From 6d64b7b41c982b99fa1f7e3359c47f2b38a09159 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 7 Jul 2026 13:10:58 +0000 Subject: [PATCH] feat(quartz): include exception type in relay connect-failure message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BasicRelayClient collapsed a connection failure into a message string built from the throwable's text alone. Message text is localized and inconsistent across platforms, so a listener can't reliably tell a busy relay (a connect timeout) from a dead one (bad domain / TLS misconfig) from it. Always append the exception class name (SocketTimeoutException / UnknownHostException / SSLHandshakeException / ConnectException …), which is stable, so listeners can classify the failure by type. Message text is preserved; the type is added in parentheses. Updated the one test that pinned the old format. --- .../relay/client/single/basic/BasicRelayClient.kt | 14 +++++++++++--- .../client/single/basic/BasicRelayClientTest.kt | 6 ++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt index 5992cecdba..b495d42dbb 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClient.kt @@ -136,7 +136,9 @@ open class BasicRelayClient( socket?.connect() } catch (e: Exception) { if (e is CancellationException) throw e - listener.onCannotConnect(this, "Error when trying to connect: ${e.message ?: e::class.simpleName}") + val typeName = e::class.simpleName + val detail = e.message?.let { "$it ($typeName)" } ?: (typeName ?: "unknown error") + listener.onCannotConnect(this, "Error when trying to connect: $detail") listener.onDisconnected(this) dontTryAgainForALongTime() markConnectionAsClosed() @@ -187,9 +189,15 @@ open class BasicRelayClient( } else { socket?.disconnect() - // suppression rules below must match the raw message; displayMsg is for listener output only + // suppression rules below must match the raw message; displayMsg is for listener output only. + // Always include the exception's class name: message text is + // localized and inconsistent across platforms, but the type + // (SocketTimeoutException / UnknownHostException / SSLHandshakeException / + // ConnectException …) is stable and lets listeners classify a failure + // reliably — a busy relay (timeout) vs a dead one (bad domain / TLS). val msg = t.message - val displayMsg = msg ?: t::class.simpleName + val typeName = t::class.simpleName + val displayMsg = if (msg != null) "$msg ($typeName)" else (typeName ?: "unknown error") // checks if this is an actual failure. Closing the socket generates an onFailure as well. // ignore tor errors. diff --git a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClientTest.kt b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClientTest.kt index 065ef0fcaf..7a7caaec58 100644 --- a/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClientTest.kt +++ b/quartz/src/commonTest/kotlin/com/vitorpamplona/quartz/nip01Core/relay/client/single/basic/BasicRelayClientTest.kt @@ -72,13 +72,15 @@ class BasicRelayClientTest { } @Test - fun onFailureWithMessageKeepsExistingFormat() { + fun onFailureWithMessageAppendsExceptionClassName() { val (socket, listener) = connectAndCapture() socket.onFailure(Exception("Connection reset"), null, null) + // The exception type is appended so listeners can classify the failure by + // its stable class name rather than by localized message text. assertEquals( - listOf("WebSocket Failure: Connection reset"), + listOf("WebSocket Failure: Connection reset (Exception)"), listener.cannotConnectMessages, ) }