From 7cffdc01271ee9941452639a5092338a233a6ed8 Mon Sep 17 00:00:00 2001 From: soffokl Date: Wed, 8 Apr 2026 11:08:57 +0600 Subject: [PATCH 1/2] Improve state channel handling to prevent panics on closed channel 2026/04/08 04:42:01 [Recovery] 2026/04/08 - 04:42:01 panic recovered: send on closed channel /usr/local/go/src/runtime/chan.go:226 (0x41131d) /usr/local/go/src/runtime/chan.go:161 (0x410db6) /go/src/github.com/mysteriumnetwork/node/services/wireguard/connection/connection.go:131 (0x12a3064) /go/src/github.com/mysteriumnetwork/node/services/wireguard/connection/connection.go:104 (0x12a2da4) /go/src/github.com/mysteriumnetwork/node/core/connection/manager.go:703 (0xe900aa) /go/src/github.com/mysteriumnetwork/node/core/connection/manager.go:292 (0xe8ac88) /go/src/github.com/mysteriumnetwork/node/core/connection/multi.go:58 (0xe92874) /go/src/github.com/mysteriumnetwork/node/tequilapi/endpoints/connection.go:207 (0x126b18a) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0xa2111a) /go/pkg/mod/github.com/mysteriumnetwork/go-rest@v0.3.1/apierror/middleware.go:28 (0xa21108) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0xa1712e) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/recovery.go:102 (0xa1711b) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0xa15f6d) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:620 (0xa15c04) /go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:576 (0xa15749) /usr/local/go/src/net/http/server.go:3301 (0x8162cd) /usr/local/go/src/net/http/server.go:2102 (0x7f4c64) /usr/local/go/src/runtime/asm_amd64.s:1700 (0x482240) --- services/wireguard/connection/connection.go | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/services/wireguard/connection/connection.go b/services/wireguard/connection/connection.go index 3657b8b00c..e99f43ea71 100644 --- a/services/wireguard/connection/connection.go +++ b/services/wireguard/connection/connection.go @@ -86,6 +86,15 @@ func (c *Connection) State() <-chan connectionstate.State { return c.stateCh } +// sendState safely sends a state to the state channel, preventing panics from sends on closed channel. +func (c *Connection) sendState(state connectionstate.State) { + select { + case c.stateCh <- state: + case <-c.done: + // Connection is stopped, don't send + } +} + // Statistics returns connection statistics channel. func (c *Connection) Statistics() (connectionstate.Statistics, error) { stats, err := c.connectionEndpoint.PeerStats() @@ -128,7 +137,7 @@ func (c *Connection) start(ctx context.Context, start startConn, options connect } }() - c.stateCh <- connectionstate.Connecting + c.sendState(connectionstate.Connecting) if options.ProviderNATConn != nil { options.ProviderNATConn.Close() @@ -170,7 +179,7 @@ func (c *Connection) start(ctx context.Context, start startConn, options connect return errors.Wrap(err, "failed while waiting for a peer handshake") } - c.stateCh <- connectionstate.Connected + c.sendState(connectionstate.Connected) return nil } @@ -224,7 +233,7 @@ func (c *Connection) GetConfig() (connection.ConsumerConfig, error) { func (c *Connection) Stop() { c.stopOnce.Do(func() { log.Info().Msg("Stopping WireGuard connection") - c.stateCh <- connectionstate.Disconnecting + c.sendState(connectionstate.Disconnecting) if c.removeAllowedIPRule != nil { c.removeAllowedIPRule() @@ -236,7 +245,11 @@ func (c *Connection) Stop() { } } - c.stateCh <- connectionstate.NotConnected + // Send final state before closing channels + select { + case c.stateCh <- connectionstate.NotConnected: + default: + } close(c.stateCh) close(c.done) From 306bed8d75c0b65749e2b5a6f822ba3aef368ea5 Mon Sep 17 00:00:00 2001 From: soffokl Date: Wed, 8 Apr 2026 11:42:08 +0600 Subject: [PATCH 2/2] Ensure safe closure of state channel to prevent panics during state sends --- services/wireguard/connection/connection.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/wireguard/connection/connection.go b/services/wireguard/connection/connection.go index e99f43ea71..7756475b49 100644 --- a/services/wireguard/connection/connection.go +++ b/services/wireguard/connection/connection.go @@ -251,7 +251,8 @@ func (c *Connection) Stop() { default: } - close(c.stateCh) + // Close done first so sendState calls will bail out instead of sending on closed stateCh close(c.done) + close(c.stateCh) }) }