snail007/goproxy · warning
client control conn not exists
Error message
client control conn not exists
What it means
The bridge periodically writes a signal byte to each client's control connection. This error is produced when a BridgeItem exists but its ClientControl connection pointer is nil, meaning the client's control channel was never established or was already closed, so the signal cannot be sent and the item is cleaned up and retried.
Source
Thrown at services/tunnel_bridge.go:155
return
}
func (s *TunnelBridge) ChnDeamon(item *BridgeItem) {
go func() {
log.Printf("%s conn chan deamon started", item.Key)
for {
var clientConn *net.Conn
var serverConn *net.Conn
serverConn = <-item.ServerChn
log.Printf("%s server conn picked up", item.Key)
OUT:
for {
_item, _ := s.br.Get(item.Key)
Item := _item.(*BridgeItem)
var err error
if Item.ClientControl != nil && *Item.ClientControl != nil {
_, err = (*Item.ClientControl).Write([]byte{'0'})
} else {
err = fmt.Errorf("client control conn not exists")
}
if err != nil {
log.Printf("%s client control conn write signal fail, err: %s, retrying...", item.Key, err)
utils.CloseConn(Item.ClientControl)
*Item.ClientControl = nil
Item.ClientControl = nil
time.Sleep(time.Second * 3)
continue
} else {
select {
case clientConn = <-item.ClientChn:
log.Printf("%s client conn picked up", item.Key)
break OUT
case <-time.After(time.Second * time.Duration(*s.cfg.Timeout*5)):
log.Printf("%s client conn picked timeout, retrying...", item.Key)
}
}
}View on GitHub (pinned to e6d6a821db)
Solutions
- Restart/reconnect the client so it re-establishes its control connection
- Check client-to-server connectivity and keep-alive settings so the control connection is not idle-killed by firewalls/NAT
- Verify the client is running a compatible version and its control port/key config matches the server
- Inspect server logs just before this error to find why the control conn was closed and address that root cause
Defensive patterns
Strategy: retry
Validate before calling
// before relying on a bridge, verify the client control channel is registered:
item, ok := bridge.Get(key)
if !ok || item.ClientControl == nil || *item.ClientControl == nil {
// wait/retry for the client to (re)register instead of signaling
time.Sleep(time.Second)
} Try / catch
// server-side is a log+retry loop; on the client, wrap reconnects:
for {
err := client.ConnectAndServe()
if err != nil {
log.Printf("control conn lost: %v; reconnecting in 3s", err)
time.Sleep(3 * time.Second)
continue
}
break
} Prevention
- Enable TCP keepalives on control connections to survive NAT/firewall idle timeouts
- Run the client under a supervisor (systemd) that restarts it on exit
- Monitor server logs for this message as an early sign of client instability
- Keep client and server versions in sync to avoid protocol registration mismatches
When it happens
Trigger: During the bridge scan loop, Item.ClientControl is nil (or points to a nil conn) when the code tries to Write the '0' signal; typically after the client disconnected, before registration completed, or after a previous failure nil-ed the pointer.
Common situations: Client behind flaky NAT/network dropped its control connection; client crashed or was restarted while server-side bridge item lingered; firewall idle-timeout killed the long-lived control connection; client never finished connecting due to wrong key/address.
Related errors
AI-assisted analysis of snail007/goproxy@e6d6a821db (2026-09-03).
Data as JSON: /api/errors/14be50d2f7f26ef3.
Report an issue: GitHub.