XTLS/Xray-core · error

cannot finish connection

Error message

cannot finish connection

What it means

Generic wrapper returned by the geodata dialer when task.Run (which encapsulates ParseDestination + tagged.Dialer) exits with any error. It is the outermost frame of the dial path; the specific cause is always in the chained Base error ('cannot understand address' or 'cannot dial remote address ...').

Source

Thrown at app/geodata/download.go:85

	dial := func(ctx context.Context, network, address string) (net.Conn, error) {
		var conn net.Conn
		err := task.Run(ctx, func() error {
			if tagged.Dialer == nil {
				return errors.New("tagged dialer is not initialized")
			}
			dest, err := net.ParseDestination(network + ":" + address)
			if err != nil {
				return errors.New("cannot understand address").Base(err)
			}
			c, err := tagged.Dialer(baseCtx, dispatcher, dest, outbound)
			if err != nil {
				return errors.New("cannot dial remote address ", dest).Base(err)
			}
			conn = c
			return nil
		})
		if err != nil {
			return nil, errors.New("cannot finish connection").Base(err)
		}
		return &idleConn{
			Conn: conn,
		}, nil
	}
	if isHTTPS {
		return &http.Client{
			Transport: &http2.Transport{
				DialTLSContext: func(ctx context.Context, network string, address string, cfg *tls.Config) (net.Conn, error) {
					conn, err := dial(ctx, network, address)
					if err != nil {
						return nil, err
					}
					host, _, _ := net.SplitHostPort(address)
					tlsConn := utls.UClient(conn, &utls.Config{ServerName: host}, utls.HelloChrome_Auto)
					handshakeCtx, cancel := context.WithTimeout(ctx, idleTimeout)
					defer cancel()
					if err := tlsConn.HandshakeContext(handshakeCtx); err != nil {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Inspect err.(*errors.Error).Base()/unwrap chain — the real cause is 'cannot understand address' or 'cannot dial remote address'
  2. Fix the underlying cause per that inner error (URL format or outbound reachability)
  3. Retry after network conditions change if the base cause is a transient connect failure
Defensive patterns

Strategy: try-catch

Try / catch

resp, err := client.Get(assetURL)
if err != nil {
    var xe *errors.Error
    if errors.As(err, &xe) && strings.Contains(xe.Message(), "cannot finish connection") {
        cause := xe.Base() // 'cannot understand address' or 'cannot dial remote address'
        log.Warn("geodata dial failed: ", cause)
    }
    return err
}

Prevention

When it happens

Trigger: Any failure inside the dial closure passed to task.Run for a geodata HTTP(S) request — address parse failure or dial failure — resurfaces from http.Client as this wrapped error.

Common situations: Same situations as errors 22 and 23: bad asset URL, missing/unreachable outbound, or network failure; users see this message first in logs and must inspect the chained cause.

Related errors


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