{"record":{"id":"4c8537a6a8ec4fe7","repo":"XTLS/Xray-core","slug":"connection-ends","errorCode":null,"errorMessage":"connection ends","messagePattern":"connection ends","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"warning","filePath":"proxy/dns/dns.go","lineNumber":307,"sourceCode":"\t\t\tb, err := connReader.ReadMessage()\n\t\t\tif err == io.EOF {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\ttimer.Update()\n\n\t\t\tif err := writer.WriteMessage(b); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tif err := task.Run(ctx, request, response); err != nil {\n\t\treturn errors.New(\"connection ends\").Base(err)\n\t}\n\n\treturn nil\n}\n\nfunc (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter, timer *signal.ActivityTimer) {\n\tvar ips []net.IP\n\tvar ttl uint32\n\tvar err error\n\n\tswitch qType {\n\tcase dnsmessage.TypeA:\n\t\tips, ttl, err = h.client.LookupIP(domain, dns.IPOption{\n\t\t\tIPv4Enable: true,\n\t\t\tIPv6Enable: false,\n\t\t\tFakeEnable: true,\n\t\t})\n\tcase dnsmessage.TypeAAAA:","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/dns/dns.go#L289-L325","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check the chained Base(err): EOF means the peer closed, RST/reset means network policy, context.Canceled means shutdown","Verify the upstream DNS server (h.nonIPQuery / server config) is reachable and answers reliably","If it occurs only at shutdown/reload, it is benign — queries die with the instance; ignore in logs","Stabilize the path (MTU, firewall rules for port 53 TCP/UDP) if resets recur"],"exampleFix":"// before: shutdown noise treated as fatal\nif err := h.handleTCP(ctx, link); err != nil { log.Fatal(err) }\n\n// after: tolerate connection teardown during shutdown/reload\nif err := h.handleTCP(ctx, link); err != nil {\n    if errors.Is(err, context.Canceled) || strings.Contains(err.Error(), \"use of closed\") {\n        return nil\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := h.handleQueryLoop(ctx, link); err != nil {\n    if errors.Is(err, context.Canceled) || isConnClosedErr(err) { return nil } // teardown is benign\n    logQueryFailure(ctx, err)\n    return err\n}","preventionTips":["Log at warn level, not fatal, for DNS connection teardown during reload","Keep upstream resolvers healthy and close (latency)","Drain contexts gracefully on shutdown so in-flight queries finish"],"tags":["go","xray","dns","connection","shutdown"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}