fatedier/frp · error
wait proxy status ready timeout
Error message
wait proxy status ready timeout
What it means
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).
Source
Thrown at pkg/ssh/server.go:401
defer timer.Stop()
statusExporter := s.vc.Service().StatusExporter()
for {
select {
case <-ticker.C:
ps, ok := statusExporter.GetProxyStatus(name)
if !ok {
continue
}
switch ps.Phase {
case proxy.ProxyPhaseRunning:
return ps, nil
case proxy.ProxyPhaseStartErr, proxy.ProxyPhaseClosed:
return ps, errors.New(ps.Err)
}
case <-timer.C:
return nil, fmt.Errorf("wait proxy status ready timeout")
case <-s.doneCh:
return nil, fmt.Errorf("ssh tunnel server closed")
}
}
}
View on GitHub (pinned to 6c8a8d0a97)
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).
Example fix
# before — two clients share port 6000 ssh v0@frps "tcp 127.0.0.1:22 --remotePort 6000" # after — unique ports per tunnel ssh v0@frps "tcp 127.0.0.1:22 --remotePort 6001"
Defensive patterns
Strategy: retry
Validate before calling
# caller-side: pick a free remote port before requesting the tunnel
# (avoids bind conflicts that stall the proxy in a non-Running phase)
python3 - <<'EOF'
import socket
s = socket.socket(); s.bind(('', 0))
print("--remotePort", s.getsockname()[1]); s.close()
EOF Try / catch
if err != nil && strings.Contains(err.Error(), "wait proxy status ready timeout") {
time.Sleep(2 * time.Second)
// re-check proxy status once via statusExporter; only then report failure
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- get addr and extra payload timeout
- ps.Err
- read newVisitorConnRespMsg error: %v
- open tunnel timeout
- dial quic error: %v
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/a5993508554c0317.
Report an issue: GitHub.