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

  1. Configure the client to use a supported command (CONNECT=1, BIND=2, UDP ASSOCIATE=3)
  2. Treat the wrapped error as a transport failure: check err.Error() for connection reset/broken pipe and retry with a fresh connection
  3. Update client SOCKS implementation if it sends non-standard command codes
  4. 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

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


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/63e74f9f5c596feb. Report an issue: GitHub.