fatedier/frp · error
open ssh channel error: %v
Error message
open ssh channel error: %v
What it means
Emitted by TunnelServer.openConn when the server cannot open a ChannelTypeServerOpenChannel back over the SSH connection to the visitor's client. For remote-visited traffic, data flows through a second SSH channel opened by frps toward the client; if the client rejects or the connection is dead, this error surfaces on the visitor's connection path.
Source
Thrown at pkg/ssh/server.go:370
return
}
case <-s.doneCh:
return
}
}
}
func (s *TunnelServer) openConn(addr *tcpipForward) (net.Conn, error) {
payload := forwardedTCPPayload{
Addr: addr.Host,
Port: addr.Port,
// Note: Here is just for compatibility, not the real source address.
OriginAddr: addr.Host,
OriginPort: addr.Port,
}
channel, reqs, err := s.sshConn.OpenChannel(ChannelTypeServerOpenChannel, ssh.Marshal(&payload))
if err != nil {
return nil, fmt.Errorf("open ssh channel error: %v", err)
}
go ssh.DiscardRequests(reqs)
conn := netpkg.WrapReadWriteCloserToConn(channel, s.underlyingConn)
return conn, nil
}
func (s *TunnelServer) waitProxyStatusReady(name string, timeout time.Duration) (*proxy.WorkingStatus, error) {
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
timer := time.NewTimer(timeout)
defer timer.Stop()
statusExporter := s.vc.Service().StatusExporter()
for {
select {View on GitHub (pinned to 6c8a8d0a97)
Solutions
- Confirm the frp SSH tunnel client is still connected and healthy (frps dashboard / logs).
- Have the visitor retry the connection — transient races during client reconnect resolve themselves.
- Keep the gateway-client connection alive with SSH keepalives/heartbeat settings so NATs do not kill it silently.
- If it persists, check client-side logs for channel-open rejections and raise channel limits if a custom SSH stack is in play.
Defensive patterns
Strategy: retry
Try / catch
// visitor-side retry with backoff when a channel open fails
var conn net.Conn
err := retry.Do(
func() error {
var e error
conn, e = dialVisitor()
return e
},
retry.OnError(func(err error) bool {
return strings.Contains(err.Error(), "open ssh channel error")
}),
retry.Attempts(3), retry.Delay(500*time.Millisecond),
) Prevention
- Keep gateway<->client connections alive (SSH keepalives) so NATs do not silently kill them.
- Expect transient channel-open failures during client reconnect windows; retry visitors.
- Monitor client liveness on frps so dead tunnels are reaped quickly.
When it happens
Trigger: Client disconnected or is shutting down right as a visitor connects; client's SSH implementation rejects the channel open; too many open channels exhausting SSH channel windows; underlying TCP connection reset between gateway and tunnel client.
Common situations: Visitor traffic racing client shutdown/restart; NAT idle timeouts killing the SSH connection while the gateway still routes to it; resource-exhausted clients dropping new channels.
Related errors
- loginRespMsg.Error
- send ${op} request to plugin error
- ps.Err
- create control crypto read writer: %w
- unexpected frame type %d, want %d
AI-assisted analysis of fatedier/frp@6c8a8d0a97 (2026-08-15).
Data as JSON: /api/errors/8f2246765830e090.
Report an issue: GitHub.