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

  1. Read the .Base(err) cause: io.EOF on one direction may be benign; net.OpError reset/timeout indicates network trouble.
  2. Increase idle timeouts on client and server if long-lived connections are being dropped.
  3. Check TUIC server logs and availability; retry transient network failures.
  4. 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

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.