thanos-io/thanos · error

alertmanagers.url contains empty scheme

Error message

alertmanagers.url contains empty scheme

What it means

BuildAlertmanagerConfig parses the alertmanagers.url and requires a URL scheme (http/https, or a dns-prefixed scheme like dns+http). An empty scheme means the URL was relative or malformed, so no HTTP client can be constructed.

Solutions

  1. Prefix the URL with its scheme: use http://alertmanager:9093 (or https://...).
  2. If the scheme must include DNS discovery, use the dns+http:// prefix form.
  3. Add config validation/linting before startup to catch scheme-less URLs.

Example fix

// before
url: alertmanager.monitoring:9093
// after
url: http://alertmanager.monitoring:9093
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(cfg.URL)
if err != nil || u.Scheme == "" {
    return fmt.Errorf("alertmanagers.url %q must include a scheme, e.g. http://host:9093", cfg.URL)
}

Prevention

When it happens

Trigger: alertmanagers.sd_configs/static_configs url like "alertmanager:9093" or ":9093" passed to BuildAlertmanagerConfig (called from runRule); url.Parse succeeds but parsed.Scheme is empty.

Common situations: Omitting http:// when writing the Alertmanager URL in rule/ruler config; config templating that renders an empty scheme prefix; port-only addresses copied from docker-compose.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/62f7e1a8ae7df910. Report an issue: GitHub.

Appendix: source

Thrown at pkg/alert/config.go:99

// LoadAlertingConfig loads a list of AlertmanagerConfig from YAML data.
func LoadAlertingConfig(confYaml []byte) (AlertingConfig, error) {
	var cfg AlertingConfig
	if err := yaml.UnmarshalStrict(confYaml, &cfg); err != nil {
		return cfg, err
	}
	return cfg, nil
}

// BuildAlertmanagerConfig initializes and returns an Alertmanager client configuration from a static address.
func BuildAlertmanagerConfig(address string, timeout time.Duration) (AlertmanagerConfig, error) {
	parsed, err := url.Parse(address)
	if err != nil {
		return AlertmanagerConfig{}, err
	}

	scheme := parsed.Scheme
	if scheme == "" {
		return AlertmanagerConfig{}, errors.New("alertmanagers.url contains empty scheme")
	}

	host := parsed.Host
	if host == "" {
		return AlertmanagerConfig{}, errors.New("alertmanagers.url contains empty host")
	}

	for _, qType := range []dns.QType{dns.A, dns.SRV, dns.SRVNoA} {
		prefix := string(qType) + "+"
		if strings.HasPrefix(strings.ToLower(scheme), prefix) {
			// Scheme is of the form "<dns type>+<http scheme>".
			scheme = strings.TrimPrefix(scheme, prefix)
			host = prefix + parsed.Host
			if qType == dns.A {
				if _, _, err := net.SplitHostPort(parsed.Host); err != nil {
					// The host port could be missing. Append the defaultAlertmanagerPort.
					host = host + ":" + strconv.Itoa(defaultAlertmanagerPort)
				}

View on GitHub (pinned to 35b8b99117)