v2rayA/v2rayA · warning
tuic connection ends
Error message
tuic connection ends
What it means
Client.Process runs two concurrent tasks — posting the request (upload) and reading the response (download) — via task.Run. If either task errors, the combined failure is wrapped as "tuic connection ends" with the original error as base. It signals the proxied connection terminated abnormally rather than a clean close.
Source
Thrown at core/hint/proxy/tuic/outbound.go:147
destAddr := fmt.Sprintf("%s:%d", destination.Address.String(), destination.Port.Value())
conn, err := c.dialer.DialContext(ctx, "tcp", destAddr)
if err != nil {
return errors.New("tuic: failed to dial destination").Base(err)
}
defer conn.Close()
postRequest := func() error {
return xray_buf.Copy(link.Reader, xray_buf.NewWriter(outboundConnWriter(conn)))
}
getResponse := func() error {
return xray_buf.Copy(xray_buf.NewReader(outboundConnReader(conn)), link.Writer)
}
responseDoneAndCloseWriter := task.OnSuccess(getResponse, task.Close(link.Writer))
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
return errors.New("tuic connection ends").Base(err)
}
return nil
}
// outboundConnReader/Writer wraps outbound_netproxy.Conn to expose io.Reader/Writer for xray's buf.
type outboundConn struct {
c outbound_netproxy.Conn
}
func outboundConnReader(c outbound_netproxy.Conn) io.Reader { return &outboundConn{c} }
func outboundConnWriter(c outbound_netproxy.Conn) io.Writer { return &outboundConn{c} }
func (n *outboundConn) Read(b []byte) (int, error) { return n.c.Read(b) }
func (n *outboundConn) Write(b []byte) (int, error) { return n.c.Write(b) }
func init() {
common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
return NewClient(ctx, config.(*ClientConfig))View on GitHub (pinned to 71e5442fc5)
Solutions
- Read the .Base(err) cause: io.EOF on one direction may be benign; net.OpError reset/timeout indicates network trouble.
- Increase idle timeouts on client and server if long-lived connections are being dropped.
- Check TUIC server logs and availability; retry transient network failures.
- Ensure the request context isn't cancelled prematurely by an overly tight deadline.
Example fix
// before
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
return errors.New("tuic connection ends").Base(err)
}
// after
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
if errors.Is(errors.Unwrap(err), io.EOF) { return nil }
return errors.New("tuic connection ends").Base(err)
} Defensive patterns
Strategy: try-catch
Try / catch
if err := task.Run(ctx, postRequest, responseDoneAndCloseWriter); err != nil {
if errors.Is(err, io.EOF) || context.Canceled == errors.Unwrap(err) {
return nil // benign close
}
var nErr net.Error
if errors.As(errors.Unwrap(err), &nErr) && nErr.Timeout() {
// reconnect/retry the tuic session
}
return errors.New("tuic connection ends").Base(err)
} Prevention
- Keep-alive/idle timeouts aligned between client and server.
- Monitor server restarts that abort in-flight tuic sessions.
- Retry idempotent transfers on transient stream errors.
When it happens
Trigger: task.Run(ctx, postRequest, responseDoneAndCloseWriter) returns non-nil: the remote side reset the connection mid-transfer, a read/write timeout fired, or the context was cancelled while copying data.
Common situations: Server closes the connection abruptly (crash, restart, idle timeout); unstable network path dropping packets; client context cancelled by an upstream deadline; protocol handshake failure surfaced during copy.
Related errors
AI-assisted analysis of v2rayA/v2rayA@71e5442fc5 (2026-09-05).
Data as JSON: /api/errors/50199721a5f67672.
Report an issue: GitHub.