XTLS/Xray-core · error · errors.Error

cannot finish connection

Error message

cannot finish connection

What it means

Thrown in app/observatory/observer.go:154 as the outer wrapper returned from DialContext when the task.Run closure (parse + dial) returned any error. It is the boundary translation between Xray's task error and Go's net.Dial return, so its Base chain contains either 'cannot understand address' or 'cannot dial remote address' plus the root cause.

Source

Thrown at app/observatory/observer.go:154

		},
		DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
			var connection net.Conn
			taskErr := task.Run(ctx, func() error {
				// MUST use Xray's built in context system
				dest, err := v2net.ParseDestination(network + ":" + addr)
				if err != nil {
					return errors.New("cannot understand address").Base(err)
				}
				trackedCtx := session.TrackedConnectionError(o.ctx, errorCollectorForRequest)
				conn, err := tagged.Dialer(trackedCtx, o.dispatcher, dest, outbound)
				if err != nil {
					return errors.New("cannot dial remote address ", dest).Base(err)
				}
				connection = conn
				return nil
			})
			if taskErr != nil {
				return nil, errors.New("cannot finish connection").Base(taskErr)
			}
			return connection, nil
		},
		TLSHandshakeTimeout: time.Second * 5,
	}
	httpClient := &http.Client{
		Transport: &httpTransport,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
		Jar:     nil,
		Timeout: time.Second * 5,
	}
	var GETTime time.Duration
	err := task.Run(o.ctx, func() error {
		startTime := time.Now()
		probeURL := "https://www.google.com/generate_204"
		if o.config.ProbeUrl != "" {

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Inspect err.Base() to find whether it was an address parse issue or a dial failure, then follow the corresponding fix.
  2. Verify the outbound server and its streamSettings.
  3. Confirm the probe URL is well-formed.
Defensive patterns

Strategy: try-catch

Try / catch

// always unwrap: the wrapper has no independent information
if err != nil {
    root := err
    for errors.Unwrap(root) != nil { root = errors.Unwrap(root) }
    handleByRootCause(root)
}

Prevention

When it happens

Trigger: Always produced together with errors 45/46: any failure of the inner parse-or-dial task surfaces to Go's http.Transport as 'cannot finish connection'. This message is the envelope; the real cause is one level down in Base().

Common situations: Appears in the middle of probe failure chains; by itself it only tells you the probe connection setup failed, and users should always read the chained error.

Related errors


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