cloudflare/cloudflared · error
Failed to send reply: %v
Error message
Failed to send reply: %v
What it means
In StandardRequestHandler.Handle, when the request's command byte is not connect, bind, or associate, the handler replies with commandNotSupported before failing. This error means writing that reply back to the client also failed, so the underlying send error (broken pipe, reset, closed socket) is reported instead of the command error.
Source
Thrown at socks/request_handler.go:45
func NewRequestHandler(dialer Dialer, accessPolicy *ipaccess.Policy) RequestHandler {
return &StandardRequestHandler{
dialer: dialer,
accessPolicy: accessPolicy,
}
}
// Handle processes and responds to socks5 commands
func (h *StandardRequestHandler) Handle(req *Request, conn io.ReadWriter) error {
switch req.Command {
case connectCommand:
return h.handleConnect(conn, req)
case bindCommand:
return h.handleBind(conn, req)
case associateCommand:
return h.handleAssociate(conn, req)
default:
if err := sendReply(conn, commandNotSupported, nil); err != nil {
return fmt.Errorf("Failed to send reply: %v", err)
}
return fmt.Errorf("Unsupported command: %v", req.Command)
}
}
// handleConnect is used to handle a connect command
func (h *StandardRequestHandler) handleConnect(conn io.ReadWriter, req *Request) error {
if h.accessPolicy != nil {
if req.DestAddr.IP == nil {
addr, err := net.ResolveIPAddr("ip", req.DestAddr.FQDN)
if err != nil {
_ = sendReply(conn, ruleFailure, req.DestAddr)
return fmt.Errorf("unable to resolve host to confirm access")
}
req.DestAddr.IP = addr.IP
}
if allowed, rule := h.accessPolicy.Allowed(req.DestAddr.IP, req.DestAddr.Port); !allowed {View on GitHub (pinned to 2253eeeb25)
Solutions
- Configure the client to use a supported command (CONNECT=1, BIND=2, UDP ASSOCIATE=3)
- Treat the wrapped error as a transport failure: check err.Error() for connection reset/broken pipe and retry with a fresh connection
- Update client SOCKS implementation if it sends non-standard command codes
- Wrap Serve in retry logic that tolerates races where the client times out and closes first
Example fix
// server side: tolerate client disconnect before reply
if err := h.Serve(conn); err != nil {
log.Warn().Err(err).Msg("socks handshake aborted by client")
} Defensive patterns
Strategy: retry
Validate before calling
// ensure the command byte is supported before connecting
if cmd := 0x01; cmd != 0x01 && cmd != 0x02 && cmd != 0x03 {
return fmt.Errorf("command %d unsupported by server", cmd)
} Type guard
func isSupportedCommand(b byte) bool { return b >= 0x01 && b <= 0x03 } Try / catch
if err := serve(conn); err != nil {
if ne, ok := err.(*net.OpError); ok || strings.Contains(err.Error(), "reset") {
time.Sleep(backoff); retry(conn)
}
} Prevention
- Use only CONNECT (0x01) unless you know BIND/UDP ASSOCIATE are enabled
- Set generous client timeouts so the client doesn't hang up before the reply
- Retry idempotent requests on a fresh connection
When it happens
Trigger: Handle receiving a command byte outside {0x01 connect, 0x02 bind, 0x03 associate} AND sendReply(conn, commandNotSupported, nil) failing to write — e.g. the client already disconnected mid-handshake.
Common situations: Aggressive clients closing the connection immediately after sending the request; network drop between greeting and reply; a client using SOCKS extensions/UDP that this handler doesn't recognize.
Related errors
- Failed to read auth methods: %v
- Failed to get command version: %v
- Unsupported command version: %v
- Failed to format address: %v
- Unrecognized address type
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/63e74f9f5c596feb.
Report an issue: GitHub.