diff --git a/nostr_core/core_relay_pool.c b/nostr_core/core_relay_pool.c index b7381b02..1b517b24 100644 --- a/nostr_core/core_relay_pool.c +++ b/nostr_core/core_relay_pool.c @@ -548,11 +548,23 @@ static void check_connection_health(relay_connection_t* relay) { now - relay->last_ping_sent >= relay->pool->reconnect_config.ping_interval_seconds && !relay->ping_pending) { - if (nostr_ws_ping(relay->ws_client) == 0) { + int ping_rc = nostr_ws_ping(relay->ws_client); + if (ping_rc == 0) { relay->last_ping_sent = now; relay->ping_pending = 1; // Store high-resolution start time for latency measurement relay->pending_ping_start_ms = get_current_time_ms(); + } else { + // Ping send failed: the underlying transport is dead (e.g. the + // remote closed the TCP connection). The ws client's cached + // state may still claim CONNECTED because it only transitions + // on a clean WebSocket CLOSE frame. Close the client so the + // cached state resets, and mark the relay disconnected so the + // reconnect logic performs a fresh connect. + nostr_ws_close(relay->ws_client); + relay->ws_client = NULL; + relay->status = NOSTR_POOL_RELAY_DISCONNECTED; + relay->ping_pending = 0; } } @@ -560,7 +572,10 @@ static void check_connection_health(relay_connection_t* relay) { if (relay->ping_pending && now - relay->last_ping_sent > relay->pool->reconnect_config.pong_timeout_seconds) { - // No pong received - connection is dead + // No pong received - connection is dead. Close the client for the + // same reason as above: the cached ws state cannot be trusted. + nostr_ws_close(relay->ws_client); + relay->ws_client = NULL; relay->status = NOSTR_POOL_RELAY_DISCONNECTED; relay->ping_pending = 0; }