XTLS/Xray-core · warning
connection ends
Error message
connection ends
What it means
Generic wrapper returned when task.Run finishes the TCP session and either the request goroutine (client→server relay) or the response goroutine (server→client relay, plus writer close) exited with an error. The Base error carries the real cause — commonly one of the sibling errors (failed to write request / payload) or a response read failure. Seeing this alone means the actual failure is one level down in the error chain.
Source
Thrown at proxy/shadowsocks/client.go:150
}
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 {
return err
}
return buf.Copy(responseReader, link.Writer, buf.UpdateActivity(timer))
}
responseDoneAndCloseWriter := task.OnSuccess(responseDone, task.Close(link.Writer))
if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
return errors.New("connection ends").Base(err)
}
return nil
}
if request.Command == protocol.RequestCommandUDP {
requestDone := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly)
writer := &UDPWriter{
Writer: conn,
Request: request,
}
if err := buf.Copy(link.Reader, writer, buf.UpdateActivity(timer)); err != nil {
return errors.New("failed to transport all UDP request").Base(err)
}View on GitHub (pinned to 7d214f8b09)
Solutions
- Inspect the wrapped (Base) error in the log line — it names which direction and which operation actually failed; fix that root cause.
- If it is io.EOF/context.Canceled on user abort, treat as normal session end, not a fault.
- For mid-stream resets, verify server stability and policy timeouts (UplinkOnly/DownlinkOnly) on both ends.
- Check for MTU/fragmentation issues if resets correlate with large uploads/downloads.
Defensive patterns
Strategy: try-catch
Try / catch
if err := task.Run(ctx, requestDone, responseDoneAndCloseWriter); err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, io.EOF) {
return nil // normal session end
}
return errors.New("connection ends").Base(err)
} Prevention
- Always unwrap the Base error before deciding severity.
- Filter context.Canceled/io.EOF from logs to reduce noise.
- Track per-outbound error rates to spot flaky servers.
When it happens
Trigger: Any TCP Shadowsocks client session where requestDone or responseDoneAndCloseWriter returns non-nil: mid-stream connection reset in either direction, ReadTCPResponse failing to parse the response header, or context cancellation while relaying.
Common situations: Server drops the session mid-transfer (idle timeout, restart); destination site resets the tunnelled connection; user cancels the request (browser stop / app close) which cancels ctx; response header decrypt failure after server-side key rotation.
Related errors
- failed to write request
- failed to write A request payload
- failed to transport all UDP request
- failed to transport all UDP response
- write encryption response: %w
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/d1fa979b81e2bbbf.
Report an issue: GitHub.