{"record":{"id":"a5993508554c0317","repo":"fatedier/frp","slug":"wait-proxy-status-ready-timeout","errorCode":null,"errorMessage":"wait proxy status ready timeout","messagePattern":"wait proxy status ready timeout","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/ssh/server.go","lineNumber":401,"sourceCode":"\tdefer timer.Stop()\n\n\tstatusExporter := s.vc.Service().StatusExporter()\n\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tps, ok := statusExporter.GetProxyStatus(name)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tswitch ps.Phase {\n\t\t\tcase proxy.ProxyPhaseRunning:\n\t\t\t\treturn ps, nil\n\t\t\tcase proxy.ProxyPhaseStartErr, proxy.ProxyPhaseClosed:\n\t\t\t\treturn ps, errors.New(ps.Err)\n\t\t\t}\n\t\tcase <-timer.C:\n\t\t\treturn nil, fmt.Errorf(\"wait proxy status ready timeout\")\n\t\tcase <-s.doneCh:\n\t\t\treturn nil, fmt.Errorf(\"ssh tunnel server closed\")\n\t\t}\n\t}\n}\n","sourceCodeStart":383,"sourceCodeEnd":407,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/pkg/ssh/server.go#L383-L407","documentation":"waitProxyStatusReady polls statusExporter every 100ms until the virtual client's proxy reaches ProxyPhaseRunning, fails (ProxyPhaseStartErr/ProxyPhaseClosed), or the caller's timeout expires. This error means the proxy was created but never reported Running within the timeout — typically because the server never finished bringing the proxy up (bind failure, port conflict, slower proxy negotiation).","triggerScenarios":"Remote port already in use so frps takes long to report the failure; large numbers of proxies making status propagation slow; timeout passed by the caller too small; proxy stuck between phases due to a stalled control connection.","commonSituations":"Two SSH tunnels grabbing the same remotePort; frps host with the port firewalled or already bound; slow links where the new-proxy workflow exceeds the wait budget.","solutions":["Check frps logs for a proxy start error — often a port bind conflict (remotePort already bound) which will also surface as ProxyPhaseStartErr once observed.","Choose a different --remotePort for the ssh tunnel command.","Retry once; races during frps proxy registration can exceed a short timeout.","If you call waitProxyStatusReady from code, pass a timeout generous enough for proxy creation (several seconds)."],"exampleFix":"# before — two clients share port 6000\nssh v0@frps \"tcp 127.0.0.1:22 --remotePort 6000\"\n\n# after — unique ports per tunnel\nssh v0@frps \"tcp 127.0.0.1:22 --remotePort 6001\"","handlingStrategy":"retry","validationCode":"# caller-side: pick a free remote port before requesting the tunnel\n# (avoids bind conflicts that stall the proxy in a non-Running phase)\npython3 - <<'EOF'\nimport socket\ns = socket.socket(); s.bind(('', 0))\nprint(\"--remotePort\", s.getsockname()[1]); s.close()\nEOF","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"wait proxy status ready timeout\") {\n    time.Sleep(2 * time.Second)\n    // re-check proxy status once via statusExporter; only then report failure\n}","preventionTips":["Allocate unique remotePorts per tunnel (central registry or ephemeral pick).","Give waitProxyStatusReady a timeout of several seconds when calling it yourself.","Check frps logs for ProxyPhaseStartErr causes (port in use, permission) before retrying blindly."],"tags":["ssh","timeout","proxy-lifecycle","port-conflict"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}