{"record":{"id":"612d8bde4470f700","repo":"netbirdio/netbird","slug":"failed-to-forward-local-wg-packet-d-to-remote-t","errorCode":null,"errorMessage":"failed to forward local WG packet (%d) to remote turn conn: %w","messagePattern":"failed to forward local WG packet \\((.+?)\\) to remote turn conn: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/iface/wgproxy/ebpf/proxy.go","lineNumber":200,"sourceCode":"\nfunc (p *WGEBPFProxy) readAndForwardPacket(buf []byte) error {\n\tn, addr, err := p.conn.ReadFromUDP(buf)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read UDP packet from WG: %w\", err)\n\t}\n\n\tp.turnConnMutex.Lock()\n\tconn, ok := p.turnConnStore[uint16(addr.Port)]\n\tp.turnConnMutex.Unlock()\n\tif !ok {\n\t\tif p.ctx.Err() == nil {\n\t\t\tlog.Debugf(\"turn conn not found by port because conn already has been closed: %d\", addr.Port)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif _, err := conn.Write(buf[:n]); err != nil {\n\t\treturn fmt.Errorf(\"failed to forward local WG packet (%d) to remote turn conn: %w\", addr.Port, err)\n\t}\n\treturn nil\n}\n\nfunc (p *WGEBPFProxy) storeTurnConn(turnConn net.Conn) (uint16, error) {\n\tp.turnConnMutex.Lock()\n\tdefer p.turnConnMutex.Unlock()\n\n\tnp, err := p.nextFreePort()\n\tif err != nil {\n\t\treturn np, err\n\t}\n\tp.turnConnStore[np] = turnConn\n\treturn np, nil\n}\n\nfunc (p *WGEBPFProxy) removeTurnConn(turnConnID uint16) {\n\tp.turnConnMutex.Lock()","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/iface/wgproxy/ebpf/proxy.go#L182-L218","documentation":"Returned by WGEBPFProxy.readAndForwardPacket when writing a locally captured WireGuard packet to the TURN (relay) connection registered for that source port fails. The turnConnStore maps an allocated local port to a net.Conn; the write is the last hop of the path local WG -> eBPF redirect -> proxy socket -> relay. Failure means that particular relay connection is broken or closed while still registered.","triggerScenarios":"Relay (TURN) server dropped the connection or restarted; NAT middlebox tore down the mapping; the peer closed its side; or the conn was closed locally but removeTurnConn has not run yet, so a packet still arrives for the stale port and Write hits a closed conn.","commonSituations":"Relay restarts or failover; long-lived relayed sessions through aggressive NATs; the moment a relayed peer disconnects while its queued packets are still being drained. Logged via log.Errorf in a loop, so it can repeat until the conn is removed.","solutions":["Check relay availability and TLS/port reachability from the host to the configured relay endpoints","Confirm the agent reconnects: the ICE/relay layer should notice the dead conn, call removeTurnConn and re-establish; if the error loops forever, restart the agent","Verify agent clock/certificate health if relay auth is failing (the underlying %w error carries the cause)","Upgrade agent if an old build kept stale conns in turnConnStore after relay failover"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := conn.Write(buf); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) || errors.Is(err, net.ErrClosed) {\n        p.removeTurnConn(port)\n        // signal the ICE/relay layer to re-establish this relay conn\n        p.onConnDead(port)\n        return nil // drop packet, path is being rebuilt\n    }\n    return fmt.Errorf(\"forward: %w\", err)\n}","preventionTips":["Monitor relay endpoint health and prefer direct connections when ICE succeeds","Remove TURN conns from the store the moment they are closed, not lazily on write failure","Alert on sustained relay write failures - they mean relayed peers are silently losing traffic"],"tags":["go","netbird","relay","turn","udp","connection-lifecycle"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}