XTLS/Xray-core · warning · errors.Error

connection ends

Error message

connection ends

What it means

In the DNS handler's TCP query loop, request and response copy tasks run under task.Run; when either task returns an error the aggregate is wrapped as "connection ends". It signals the client- or server-side connection terminated before the DNS exchange completed cleanly — EOF mid-query, read/write failure, or the UDP/TCP relay closing.

Source

Thrown at proxy/dns/dns.go:307

			b, err := connReader.ReadMessage()
			if err == io.EOF {
				return nil
			}

			if err != nil {
				return err
			}

			timer.Update()

			if err := writer.WriteMessage(b); err != nil {
				return err
			}
		}
	}

	if err := task.Run(ctx, request, response); err != nil {
		return errors.New("connection ends").Base(err)
	}

	return nil
}

func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {
	var ips []net.IP
	var ttl uint32
	var err error

	switch qType {
	case dnsmessage.TypeA:
		ips, ttl, err = h.client.LookupIP(domain, dns.IPOption{
			IPv4Enable: true,
			IPv6Enable: false,
			FakeEnable: true,
		})
	case dnsmessage.TypeAAAA:

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Check the chained Base(err): EOF means the peer closed, RST/reset means network policy, context.Canceled means shutdown
  2. Verify the upstream DNS server (h.nonIPQuery / server config) is reachable and answers reliably
  3. If it occurs only at shutdown/reload, it is benign — queries die with the instance; ignore in logs
  4. Stabilize the path (MTU, firewall rules for port 53 TCP/UDP) if resets recur

Example fix

// before: shutdown noise treated as fatal
if err := h.handleTCP(ctx, link); err != nil { log.Fatal(err) }

// after: tolerate connection teardown during shutdown/reload
if err := h.handleTCP(ctx, link); err != nil {
    if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "use of closed") {
        return nil
    }
    return err
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := h.handleQueryLoop(ctx, link); err != nil {
    if errors.Is(err, context.Canceled) || isConnClosedErr(err) { return nil } // teardown is benign
    logQueryFailure(ctx, err)
    return err
}

Prevention

When it happens

Trigger: DNS client disconnects before the answer arrives; the remote DNS endpoint resets the connection; socket read/write errors inside the relay goroutines; context cancellation while a query is in flight during shutdown/reload.

Common situations: Short browser timeouts racing slow upstream resolvers; firewall RST-ing long-lived DNS-over-TCP; Xray instance shutting down (config reload) while queries are outstanding; flaky mobile networks.

Related errors


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