XTLS/Xray-core · error

connection ends

Error message

connection ends

What it means

Thrown by the SOCKS outbound client (proxy/socks/client.go:171) when task.Run, which concurrently runs the uplink copy (link.Reader -> SOCKS connection / UDP writer) and the downlink copy (SOCKS connection / UDP reader -> link.Writer), returns an error. It is a generic wrapper: 'connection ends' means the proxied tunnel terminated before normal completion. The Base error carries the real cause, such as idle-timeout cancellation (signal.CancelAfterInactivity), a TCP RST/EOF from the remote SOCKS server, or a UDP dial/read failure.

Source

Thrown at proxy/socks/client.go:171

			defer timer.SetTimeout(p.Timeouts.DownlinkOnly)
			writer := &UDPWriter{Writer: udpConn, Request: request}
			return buf.Copy(link.Reader, writer, buf.UpdateActivity(timer))
		}
		responseFunc = func() error {
			ob.CanSpliceCopy = 1
			defer timer.SetTimeout(p.Timeouts.UplinkOnly)
			reader := &UDPReader{Reader: udpConn}
			return buf.Copy(reader, link.Writer, buf.UpdateActivity(timer))
		}
	}

	if newCtx != nil {
		ctx = newCtx
	}

	responseDonePost := task.OnSuccess(responseFunc, task.Close(link.Writer))
	if err := task.Run(ctx, requestFunc, responseDonePost); err != nil {
		return errors.New("connection ends").Base(err)
	}

	return nil
}

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 7d214f8b09)

Solutions

  1. Inspect the wrapped error (err via errors.Unwrap / log output) to find the underlying cause before changing anything.
  2. If connections die during idle periods, raise connectionIdle/downlinkOnly/uplinkOnly timeouts in the policy settings of the outbound's streamSettings/user level.
  3. If the remote SOCKS server resets the connection, check that server's logs and health; verify the address/credentials in the outbound config.
  4. For UDP failures, confirm the remote server supports UDP associate and the network path allows UDP (no blocking NAT/firewall).
  5. Treat as transient and reconnect at the application layer if the base error is a timeout or reset.

Example fix

// before: policy with aggressive idle timeout causes frequent 'connection ends'
"policy": { "levels": { "0": { "handshake": 4, "connIdle": 30, "uplinkOnly": 0, "downlinkOnly": 0 } } }

// after: allow idle periods without killing the tunnel
"policy": { "levels": { "0": { "handshake": 4, "connIdle": 300, "uplinkOnly": 60, "downlinkOnly": 60 } } }
Defensive patterns

Strategy: retry

Try / catch

// In Go, check err from the outbound Process path and inspect the chain
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) || strings.Contains(err.Error(), "connection ends") {
        // transient tunnel termination: reconnect / retry request
        return retryWithBackoff()
    }
    return err
}

Prevention

When it happens

Trigger: Calling the SOCKS outbound Process() for a TCP or UDP request and having any of these fail: buf.Copy in requestFunc or responseFunc, the UDP dial in the UDP branch, or the context being cancelled by the inactivity timer (policy timeouts: connectionIdle, downlinkOnly, upplinkOnly). Also triggered when the remote SOCKS server closes the connection mid-stream.

Common situations: Aggressive policy timeouts (small connectionIdle values) killing quiet connections; remote proxy server restarting or dropping the session; flaky NAT dropping long-lived UDP sessions; client-side cancellation of a request; network switch during an active connection.

Related errors


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