XTLS/Xray-core · error

failed to write request

Error message

failed to write request

What it means

Thrown while establishing a Shadowsocks client TCP session: the outbound request header (target address, cipher/AEAD framing) could not be written to the remote Shadowsocks server connection via WriteTCPRequest. It wraps the underlying transport error, so the root cause is almost always a broken, reset, or timed-out socket rather than a protocol logic bug. The connection is aborted before any payload is relayed.

Source

Thrown at proxy/shadowsocks/client.go:123

	ctx, cancel := context.WithCancel(ctx)
	timer := signal.CancelAfterInactivity(ctx, func() {
		cancel()
		if newCancel != nil {
			newCancel()
		}
	}, sessionPolicy.Timeouts.ConnectionIdle)

	if newCtx != nil {
		ctx = newCtx
	}

	if request.Command == protocol.RequestCommandTCP {
		requestDone := func() error {
			defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
			bufferedWriter := buf.NewBufferedWriter(buf.NewWriter(conn))
			bodyWriter, err := WriteTCPRequest(request, bufferedWriter)
			if err != nil {
				return errors.New("failed to write request").Base(err)
			}

			if err = buf.CopyOnceTimeout(link.Reader, bodyWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout {
				return errors.New("failed to write A request payload").Base(err).AtWarning()
			}

			if err := bufferedWriter.SetBuffered(false); err != nil {
				return err
			}

			return buf.Copy(link.Reader, bodyWriter, buf.UpdateActivity(timer))
		}

		responseDone := func() error {
			defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly)

			responseReader, err := ReadTCPResponse(user, conn)
			if err != nil {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Verify the remote Shadowsocks server is up and the address/port/password in the outbound config are correct.
  2. Test raw reachability from the Xray host (e.g. curl telnet://host:port or nc) to rule out firewall/NAT reset.
  3. Check server logs at the same timestamp — a reset on accept usually appears there; fix server-side crash or cipher mismatch.
  4. Review policy timeouts (connIdle) so the conn is not reaped before the request header is written.
  5. Capture with tcpdump to confirm whether the FIN/RST comes from the network path or the server.

Example fix

// before
"outbounds": [{
  "protocol": "shadowsocks",
  "settings": { "servers": [{ "address": "ss.example.com", "port": 443, "method": "aes-256-gcm", "password": "wrong-or-right" }] }
}]
// after: confirm server listens on the same port/method, e.g. on the server
// ss -lntp | grep 443  and check the server's own xray log for the reset
Defensive patterns

Strategy: retry

Try / catch

if err := outbound.Dispatch(ctx, link); err != nil {
  if errors.Is(err, io.EOF) || strings.Contains(err.Error(), "failed to write request") {
    // transient transport failure: re-dial once with backoff
    return retryOnce(ctx, outbound, link)
  }
  return err
}

Prevention

When it happens

Trigger: Calling the Shadowsocks outbound handler for a TCP request where WriteTCPRequest fails: remote server refuses/resets the connection after accept, TLS/transport handshake fails mid-write, dialer-provided conn is already closed, or AEAD encoding of the address header returns an error.

Common situations: Shadowsocks server port reachable but process crashing on accept; firewall or middlebox killing the stream right after connect; outbound conn handed to the proxy already timed out by a short ConnectionIdle policy; server behind NAT with stale port-forward.

Related errors


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/61f8fb67e3037dcc. Report an issue: GitHub.