{"record":{"id":"1848c001c5538b2a","repo":"router-for-me/CLIProxyAPI","slug":"listen-for-codex-live-tcp-proxy-candidate-w","errorCode":null,"errorMessage":"listen for Codex live TCP proxy candidate: %w","messagePattern":"listen for Codex live TCP proxy candidate: %w","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/client/codex/live/tcp_proxy.go","lineNumber":304,"sourceCode":"\treturn errors.Join(closeErrors...)\n}\n\nfunc newTCPCandidateTunnel(target netip.AddrPort, dialer proxy.ContextDialer, expectedUser, remotePassword string) (*tcpCandidateTunnel, error) {\n\tif !isPublicProxyTarget(target.Addr()) || target.Port() != 443 {\n\t\treturn nil, errors.New(\"Codex live TCP proxy target is not allowed\")\n\t}\n\tif dialer == nil || strings.TrimSpace(expectedUser) == \"\" || strings.TrimSpace(remotePassword) == \"\" {\n\t\treturn nil, errors.New(\"Codex live TCP proxy tunnel configuration is incomplete\")\n\t}\n\tnetwork := \"tcp4\"\n\tlistenAddress := \"127.0.0.1:0\"\n\tif target.Addr().Is6() {\n\t\tnetwork = \"tcp6\"\n\t\tlistenAddress = \"[::1]:0\"\n\t}\n\tlistener, errListen := net.Listen(network, listenAddress)\n\tif errListen != nil {\n\t\treturn nil, fmt.Errorf(\"listen for Codex live TCP proxy candidate: %w\", errListen)\n\t}\n\ttunnelContext, cancelTunnel := context.WithCancel(context.Background())\n\ttunnel := &tcpCandidateTunnel{\n\t\tlistener:        listener,\n\t\ttarget:          target,\n\t\tdialer:          dialer,\n\t\texpectedUser:    expectedUser,\n\t\tremotePassword:  remotePassword,\n\t\tconnections:     make(map[net.Conn]struct{}),\n\t\tvalidationSlots: make(chan struct{}, maxUnauthenticatedTCPConns),\n\t\tctx:             tunnelContext,\n\t\tcancel:          cancelTunnel,\n\t}\n\tgo tunnel.accept()\n\treturn tunnel, nil\n}\n\nfunc (t *tcpCandidateTunnel) accept() {","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/client/codex/live/tcp_proxy.go#L286-L322","documentation":"For each proxied candidate the proxy opens a loopback TCP listener (127.0.0.1:0 or [::1]:0) that the local ICE agent connects to. This error means net.Listen on loopback failed — the OS refused a new listener, typically due to file-descriptor exhaustion or loopback networking being unavailable in the runtime environment.","triggerScenarios":"startTCPCandidateTunnel calls net.Listen(\"tcp4\", \"127.0.0.1:0\") (or tcp6/[::1]:0) and it returns an error: EMFILE (fd limit reached), ENOBUFS, or no loopback interface in a restricted container/sandbox.","commonSituations":"ulimit -n too low for many sessions x tunnels; container with network namespace lacking lo; IPv6 disabled so [::1] listen fails for TCP6 targets; heavy session churn leaking listeners.","solutions":["Check 'ulimit -n' and raise the fd limit (each tunnel holds a listener plus accepted conns).","Verify loopback exists in the container ('ip addr show lo'); enable networking in the sandbox.","For IPv6 targets failing on hosts without IPv6, allow the tunnel to fall back to a tcp4 listener carrying an IPv6-mapped target, or disable IPv6 upstream candidates.","Look for listener leaks — tunnels are closed via closeTunnels; confirm error paths actually reach it."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check loopback availability and fd headroom before starting a session\nfunc canOpenLoopbackListener() bool {\n\tl, err := net.Listen(\"tcp4\", \"127.0.0.1:0\")\n\tif err != nil { return false }\n\t_ = l.Close()\n\treturn true\n}","typeGuard":null,"tryCatchPattern":"tunnel, err := startTCPCandidateTunnel(...)\nif err != nil {\n\tif strings.Contains(err.Error(), \"listen for Codex live TCP proxy candidate\") {\n\t\ttime.Sleep(100 * time.Millisecond) // transient EMFILE/ENOBUFS\n\t\ttunnel, err = startTCPCandidateTunnel(...)\n\t}\n\tif err != nil { return err }\n}","preventionTips":["Raise RLIMIT_NOFILE in service units (LimitNOFILE=65536) — each tunnel holds listeners and conns.","Ensure containers have a loopback interface and IPv6 when proxying TCP6 candidates.","Load-test session churn to confirm listeners are not leaked."],"tags":["network","tcp","listen","file-descriptors","codex-live","tcp-proxy"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}