thanos-io/thanos · error

parsing meta-monitoring URL

Error message

parsing meta-monitoring URL

What it means

After parsing the config, ParseRootLimitConfig validates the writeLimits.globalLimits.metaMonitoringURL field with url.Parse. url.Parse accepts bare paths, so this error plus the explicit scheme/host check guards against a URL that would not be usable as an HTTP endpoint.

Solutions

  1. Read the wrapped url.Parse error for the specific syntax problem and fix the URL
  2. Ensure the value includes both a scheme and host, e.g. http://metrics.local:9090
  3. Check kubernetes/helm values so templating does not inject literal quotes or 'null'
  4. If no meta-monitoring endpoint is desired, leave metaMonitoringURL empty instead of a placeholder

Example fix

# before
writeLimits:
  globalLimits:
    metaMonitoringURL: "thanos-metrics.local/api/v1/write"
# after
writeLimits:
  globalLimits:
    metaMonitoringURL: "http://thanos-metrics.local/api/v1/write"
Defensive patterns

Strategy: validation

Validate before calling

func validMetaMonitoringURL(u string) bool {
    if u == "" { return true }
    parsed, err := url.Parse(u)
    return err == nil && parsed.Host != "" && parsed.Scheme != ""
}
// call before constructing RootLimitsConfig

Type guard

func isUsableURL(u *url.URL) bool { return u != nil && u.Scheme != "" && u.Host != "" }

Try / catch

cfg, err := ParseRootLimitConfig(content)
if err != nil {
    if strings.Contains(err.Error(), "meta-monitoring URL") {
        log.Fatalf("fix metaMonitoringURL in config: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: Setting metaMonitoringURL to a value without a scheme (e.g. 'localhost:8080' parses oddly, 'metrics.local/meta'), or a string that url.Parse rejects entirely (invalid characters, unmatched brackets, e.g. 'http://[::1').

Common situations: Operator forgets http:// or https:// prefix; copies a URL with quotes or spaces from docs; uses DNS-only name with a path; misconfigured Helm values producing 'null' or empty-ish URL strings.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at pkg/receive/limiter_config.go:32

// RootLimitsConfig is the root configuration for limits.
type RootLimitsConfig struct {
	// WriteLimits hold the limits for writing data.
	WriteLimits WriteLimitsConfig `yaml:"write"`
}

// ParseRootLimitConfig parses the root limit configuration. Even though
// the result is a pointer, it will only be nil if an error is returned.
func ParseRootLimitConfig(content []byte) (*RootLimitsConfig, error) {
	var root RootLimitsConfig
	if err := yaml.UnmarshalStrict(content, &root); err != nil {
		return nil, errors.Wrapf(err, "parsing config YAML file")
	}

	if root.WriteLimits.GlobalLimits.MetaMonitoringURL != "" {
		u, err := url.Parse(root.WriteLimits.GlobalLimits.MetaMonitoringURL)
		if err != nil {
			return nil, errors.Wrapf(err, "parsing meta-monitoring URL")
		}

		// url.Parse might pass a URL with only path, so need to check here for scheme and host.
		// As per docs: https://pkg.go.dev/net/url#Parse.
		if u.Host == "" || u.Scheme == "" {
			return nil, errors.Newf("%s is not a valid meta-monitoring URL (scheme: %s,host: %s)", u, u.Scheme, u.Host)
		}
		root.WriteLimits.GlobalLimits.metaMonitoringURL = u
	}

	// Set default query if none specified.
	if root.WriteLimits.GlobalLimits.MetaMonitoringLimitQuery == "" {
		root.WriteLimits.GlobalLimits.MetaMonitoringLimitQuery = "sum(prometheus_tsdb_head_series) by (tenant)"
	}

	return &root, nil
}

View on GitHub (pinned to 35b8b99117)