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
- Check whether it co-occurs with [725]/[726] at session close — then it is normal teardown noise
- If it fires mid-transfer, raise Timeouts.ConnectionIdle in the policy applied to the user's level
- 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
- Set policy connIdle above your workload's longest silent gap
- Let task.Run aggregate both pipes rather than handling mid-relay recovery manually
- Treat paired request/response copy failures at close time as normal teardown
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
- failed to write response
- unexpected EOF
- connection ends
- failed to fallback request payload
- failed to deliver response payload
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/be189cae5161a650.
Report an issue: GitHub.