thanos-io/thanos · error

tenant name not valid

Error message

tenant name not valid

What it means

IsTenantValid validates a multi-tenancy tenant ID by ensuring it is a single path element (tenant == path.Base(tenant)). Tenant IDs are used in object storage paths, so values containing '/' or being '.' would break storage layout or escape directories.

Solutions

  1. Send the correct tenant header (e.g. THanos-Scope: <id>) with a simple identifier containing no '/' and not empty.
  2. Validate the tenant at configuration/startup: call tenancy.IsTenantValid(tenant) and fail fast with a clear message.
  3. If you need hierarchical tenants, encode the hierarchy without '/' (e.g. use '-' separator).
  4. Check reverse proxy/gateway config so it forwards the tenant header verbatim and doesn't inject request paths.

Example fix

// before
tenant := r.URL.Path // "org/team"
// after
tenant := r.Header.Get("THanos-Scope") // "org-team"
if err := tenancy.IsTenantValid(tenant); err != nil { http.Error(w, err.Error(), http.StatusBadRequest); return }
Defensive patterns

Strategy: validation

Validate before calling

func tenantOK(t string) bool { return t != "" && t == path.Base(t) }

Try / catch

if err := tenancy.IsTenantValid(tenant); err != nil {
    http.Error(w, "invalid tenant", http.StatusBadRequest)
    return
}

Prevention

When it happens

Trigger: IsTenantValid(tenant) returns this error when tenant != path.Base(tenant) — e.g. tenant contains '/', is empty (path.Base("") == "."), or equals ".".

Common situations: Missing tenant header causing empty string; a downstream proxy appending path-like IDs ("org/team") as tenants; URL parsing pulling tenant from a path segment including slashes; clients configured with wrong tenant header name.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at pkg/tenancy/tenancy.go:43

	DefaultTenant = "default-tenant"
	// DefaultTenantLabel is the default label-name with which the tenant is announced in stored metrics.
	DefaultTenantLabel = "tenant_id"
	// This key is used to pass tenant information using Context.
	TenantKey contextKey = 0
	// MetricLabel is the label name used for adding tenant information to exported metrics.
	MetricLabel = "tenant"
)

// Allowed fields in client certificates.
const (
	CertificateFieldOrganization       = "organization"
	CertificateFieldOrganizationalUnit = "organizationalUnit"
	CertificateFieldCommonName         = "commonName"
)

func IsTenantValid(tenant string) error {
	if tenant != path.Base(tenant) {
		return errors.New("tenant name not valid")
	}
	return nil
}

// GetTenantFromHTTP extracts the tenant from a http.Request object.
func GetTenantFromHTTP(r *http.Request, tenantHeader string, defaultTenantID string, certTenantField string) (string, error) {
	var err error
	tenant := r.Header.Get(tenantHeader)
	if tenant == "" {
		tenant = r.Header.Get(DefaultTenantHeader)
		if tenant == "" {
			tenant = defaultTenantID
		}
	}

	if certTenantField != "" {
		tenant, err = getTenantFromCertificate(r, certTenantField)
		if err != nil {

View on GitHub (pinned to 35b8b99117)