XTLS/Xray-core · warning · errors.Error

failed to produce report

Error message

failed to produce report

What it means

Returned by errorCollector.UnderlyingError() in app/observatory/explainErrors.go:23 when the probe's HTTP request failed but no connection-level error was ever recorded in the collector. That means the failure happened at a layer Xray's session tracking does not instrument (for example TLS handshake timeout, HTTP 5xx-style transport error, or response-read failure), so there is no underlying connection report to show.

Source

Thrown at app/observatory/explainErrors.go:23

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. Treat this as 'no connection-level diagnosis available' and debug at the transport layer: try the probe URL manually with curl from the outbound host.
  2. Set observatory ProbeUrl to a plain-HTTP-reachable or more reliable endpoint (default is https://www.google.com/generate_204) — in blocked regions Google probes fail without a clear connection error.
  3. Check whether the outbound's TLS/streamSettings are valid; handshake failures often bypass connection tracking.
  4. Increase client timeout context if probes are slow but alive.

Example fix

// before
"observatory": {"subjectSelector": ["out"]}

// after — probe a reachable URL to get meaningful diagnostics
"observatory": {
  "subjectSelector": ["out"],
  "probeURL": "https://www.gstatic.com/generate_204"
}
Defensive patterns

Strategy: validation

Validate before calling

// choose a probe URL known-reachable through your outbounds before relying on diagnostics
if observatoryCfg.ProbeUrl == "" && inCensoredRegion() {
    observatoryCfg.ProbeUrl = "https://www.gstatic.com/generate_204"
}

Prevention

When it happens

Trigger: The HTTP client's Do() returns an error while DialContext either succeeded or failed before any tracked connection error was submitted (e.g. TLSHandshakeTimeout of 5s exceeded, or context deadline). The code then asks errorCollectorForRequest.UnderlyingError() and gets this placeholder.

Common situations: Observatory logs where an outbound is reported dead 'with outbound handler report underlying connection failed' but the explanation is just 'failed to produce report' — typical for MITM'd networks, broken TLS to the probe URL, or probes killed by the 5-second client Timeout.

Related errors


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