gofiber/fiber · error

Domain pattern cannot be empty

Error message

Domain pattern cannot be empty

What it means

Panics from domain.go:63 inside parseDomainPattern when the domain pattern is empty after TrimSpace and trimming trailing dots. Domain patterns drive host-based routing via app.Domain(pattern) / group.Domain(pattern); an empty pattern would match every host and shadow all other routes, so it is rejected.

Source

Thrown at domain.go:63

// maxDomainParts defines the maximum number of domain labels allowed (e.g., sub.domain.example.com = 4 parts).
// This prevents DoS attacks from patterns or hostnames with excessive label counts.
// RFC 1035 suggests 127 labels max, but we use a more conservative limit to prevent memory exhaustion.
const maxDomainParts = 16

// parseDomainPattern parses a domain pattern like ":subdomain.example.com"
// into a domainMatcher. Parameter parts start with ":".
// Constant labels are lowercased per RFC 4343 (domain names are case-insensitive),
// but parameter names are preserved as-is so that DomainParam lookups work with
// the exact names the caller used (e.g., ":User" → param name "User").
func parseDomainPattern(pattern string) domainMatcher {
	pattern = utils.TrimSpace(pattern)
	// Trim trailing dot of a fully-qualified domain name (RFC 3986),
	// consistent with Fiber's own host normalization in Subdomains().
	pattern = utils.TrimRight(pattern, '.')

	// Validate pattern is not empty after trimming
	if pattern == "" {
		panic("Domain pattern cannot be empty")
	}

	// Enforce RFC 1035 total length limit on patterns
	if len(pattern) > 253 {
		panic(fmt.Sprintf("Domain pattern '%s' exceeds RFC 1035 maximum of 253 characters (%d chars)",
			pattern, len(pattern)))
	}

	parts := strings.Split(pattern, ".")

	// Prevent DoS from patterns with excessive label counts
	if len(parts) > maxDomainParts {
		panic(fmt.Sprintf("Domain pattern '%s' has %d parts, which exceeds the maximum of %d",
			pattern, len(parts), maxDomainParts))
	}

	m := domainMatcher{
		parts:    make([]string, len(parts)),

View on GitHub (pinned to 9a4c7e57fe)

Solutions

  1. Provide a concrete non-empty domain pattern such as app.Domain("api.example.com").
  2. Validate the pattern is non-empty before calling Domain(); skip domain-scoped routes when unset.
  3. Default the env var to a real hostname at config load time.

Example fix

// before
host := os.Getenv("TENANT_HOST") // ""
app.Domain(host).Get("/", h)

// after
host := os.Getenv("TENANT_HOST")
if strings.TrimSpace(host) != "" {
    app.Domain(host).Get("/", h)
} else {
    app.Get("/", h) // fallback, non-domain-scoped
}
Defensive patterns

Strategy: validation

Validate before calling

host := strings.TrimSpace(os.Getenv("TENANT_HOST"))
host = strings.TrimRight(host, ".")
if host == "" {
    return fmt.Errorf("TENANT_HOST must be set to a non-empty domain")
}
app.Domain(host).Get("/", h)

Prevention

When it happens

Trigger: app.Domain("") , app.Domain(" "), app.Domain("...") (all dots trimmed away), or app.Domain(strings.TrimSuffix(host, host)) that yields empty.

Common situations: Reading the domain from an env var that is unset; config-driven multi-tenant host patterns where a tenant has no domain configured; a default/empty string slipping through validation upstream.

Related errors


AI-assisted analysis of gofiber/fiber@9a4c7e57fe (2026-08-04). Data as JSON: /data/errors/bab742972b8dd379.json. Report an issue: GitHub.