XTLS/Xray-core · warning · errors.Error

underlying connection error

Error message

underlying connection error

What it means

Created in app/observatory/explainErrors.go:11 by errorCollector.SubmitError: it is the wrapper label prepended to the first underlying connection error recorded during an observatory probe. Xray's session system tracks connection errors through the dispatch path, and this collector chains them so the final probe report can explain why a connection died.

Source

Thrown at app/observatory/explainErrors.go:11

package observatory

import "github.com/xtls/xray-core/common/errors"

type errorCollector struct {
	errors *errors.Error
}

func (e *errorCollector) SubmitError(err error) {
	if e.errors == nil {
		e.errors = errors.New("underlying connection error").Base(err)
		return
	}
	e.errors = e.errors.Base(errors.New("underlying connection error").Base(err))
}

func newErrorCollector() *errorCollector {
	return &errorCollector{}
}

func (e *errorCollector) UnderlyingError() error {
	if e.errors == nil {
		return errors.New("failed to produce report")
	}
	return e.errors
}

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Read the chained error (Base) — run err with %v in a multi-error-aware logger or inspect errors.Error path — to find the true cause.
  2. Fix the underlying outbound: check server reachability, credentials, and TLS settings of the proxy being probed.
  3. If the probe target itself is unreachable from the server, set probeUrl in the observatory config to a host you can reach.
Defensive patterns

Strategy: fallback

Try / catch

// consumers of observatory results: this is a warning-chain wrapper, not a failure to handle
if result.Alive { keepOutbound() } else { logChainedErrors(result.LastErrorReason) }

Prevention

When it happens

Trigger: Any outbound connection attempt made while probing an outbound (via session.TrackedConnectionError contexts) reports an error back to the collector — e.g. dial failure, TLS failure, or an upstream proxy refusing the connection. This wrapper becomes the head of that error chain.

Common situations: Almost always seen inside 'the outbound X is dead' log lines from the observatory; the useful information is the chained inner error (dial timeout, SOCKS rejection, TLS alert), not this wrapper text itself.

Related errors


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