XTLS/Xray-core · error

failed to transfer request

Error message

failed to transfer request

What it means

Copying the client's uplink bytes to the dispatched outbound link failed (buf.Copy from clientReader to link.Writer returned non-nil). This is the request-direction pipe of the trojan relay; it dies when the client connection breaks or the outbound writer errors.

Source

Thrown at proxy/trojan/server.go:340

func (s *Server) handleConnection(ctx context.Context, sessionPolicy policy.Session,
	destination net.Destination,
	clientReader buf.Reader,
	clientWriter buf.Writer, dispatcher routing.Dispatcher,
) error {
	ctx, cancel := context.WithCancel(ctx)
	timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle)
	ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer)

	link, err := dispatcher.Dispatch(ctx, destination)
	if err != nil {
		return errors.New("failed to dispatch request to ", destination).Base(err)
	}

	requestDone := func() error {
		defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
		if buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)) != nil {
			return errors.New("failed to transfer request").Base(err)
		}
		return nil
	}

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

		if err := buf.Copy(link.Reader, clientWriter, buf.UpdateActivity(timer)); err != nil {
			return errors.New("failed to write response").Base(err)
		}
		return nil
	}

	requestDonePost := task.OnSuccess(requestDone, task.Close(link.Writer))
	if err := task.Run(ctx, requestDonePost, responseDone); err != nil {
		common.Must(common.Interrupt(link.Reader))
		common.Must(common.Interrupt(link.Writer))
		return errors.New("connection ends").Base(err)

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Check whether it co-occurs with [725]/[726] at session close — then it is normal teardown noise
  2. If it fires mid-transfer, raise Timeouts.ConnectionIdle in the policy applied to the user's level
  3. Inspect the Base error to distinguish client-side read failure from outbound write failure

Example fix

// json policy: give long-lived sessions more idle headroom
"policy": {"levels": {"0": {"handshake": 4, "connIdle": 300}}, "system": {"statsOutboundUplink": true}}
Defensive patterns

Strategy: try-catch

Try / catch

if err := buf.Copy(clientReader, link.Writer, buf.UpdateActivity(timer)); err != nil {
    // pipe broke (client gone or outbound closed): end this direction only
    return errors.New("failed to transfer request").Base(err)
}

Prevention

When it happens

Trigger: Client disconnects abruptly (RST) mid-transfer, outbound writer is closed by the other pipe finishing first, or an i/o timeout from policy Timeouts.ConnectionIdle fires via the inactivity timer.

Common situations: Normal connection teardown races (one side closes first), flaky client networks, or idle timeouts shorter than the workload's silence gaps.

Related errors


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