gofiber/fiber · error

Domain pattern ' ' contains invalid character '%c' in label

Error message

Domain pattern '%s' contains invalid character '%c' in label '%s'

What it means

A constant label in a domain pattern contains a character that is not a lowercase letter, digit, or hyphen after lowercasing (RFC 4343 lowercasing is applied first). Underscores, dots, spaces, punctuation, and non-ASCII characters are rejected because they are invalid in DNS hostnames. Note this applies to constant labels only — ':param' names additionally permit '_'.

Solutions

  1. Remove underscores and other punctuation from constant labels; use hyphens instead (e.g. 'foo-bar' not 'foo_bar').
  2. Punycode-encode any internationalized label (xn--...) before putting it in the pattern.
  3. If the underscore is intentional (e.g. _acme-challenge), route it as a ':param' segment or handle it outside the domain matcher.

Example fix

// before
app.Use("my_api.example.com", handler)
// after
app.Use("my-api.example.com", handler)
Defensive patterns

Strategy: validation

Validate before calling

// validConstantLabel reports whether a literal label is DNS-safe (a-z0-9-).
func validConstantLabel(lbl string) bool {
    if lbl == "" || len(lbl) > 63 {
        return false
    }
    for _, ch := range strings.ToLower(lbl) {
        if !((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-') {
            return false
        }
    }
    return true
}

for _, lbl := range strings.Split(domainPattern, ".") {
    if strings.HasPrefix(lbl, ":") {
        continue
    }
    if !validConstantLabel(lbl) {
        return fmt.Errorf("invalid constant label %q", lbl)
    }
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        log.Fatalf("invalid domain label: %v", r)
    }
}()
app.Use(domainPattern, handler)

Prevention

When it happens

Trigger: Passing a domain pattern whose literal labels contain '_', '.', ' ', ':', '@', or non-ASCII characters, e.g. app.Use("foo_bar.example.com", ...), app.Use("café.example.com", ...), app.Use("foo!bar.com", ...). The underscore in particular is allowed in param names but NOT in constant labels.

Common situations: Using snake_case hostnames (common in internal tooling/SRV records) as constant labels; pasting internationalized domain names without IDNA/punycode encoding; copy-paste introducing a trailing space or hidden unicode character; confusing param-name rules (which allow '_') with constant-label rules (which do not).

Understand the failure class

Related errors


AI-assisted analysis of gofiber/fiber@a105acad6c (2026-08-11). Data as JSON: /api/errors/a4bbfe77b1664a2e. Report an issue: GitHub.

Appendix: source

Thrown at domain.go:119

				if !isASCIIAlphanumeric(ch) && ch != '_' && ch != '-' {
					panic(fmt.Sprintf("Domain pattern '%s' contains invalid parameter name '%s' with character '%c'", pattern, paramName, ch))
				}
			}
			m.paramIdx = append(m.paramIdx, i)
			m.paramNames = append(m.paramNames, paramName) // preserve original case
			m.parts[i] = part                              // keep ":param" marker for matching
		} else {
			// Only lowercase constant labels (RFC 4343)
			// Enforce RFC 1035 per-label length limit (63 characters)
			if len(part) > 63 {
				panic(fmt.Sprintf("Domain pattern '%s' has label '%s' exceeding RFC 1035 limit of 63 characters (%d chars)",
					pattern, part, len(part)))
			}
			// Validate label contains only valid ASCII domain characters (a-z, 0-9, hyphen).
			normalized := utilsstrings.ToLower(part)
			for _, ch := range normalized {
				if !isASCIIAlphanumeric(ch) && ch != '-' {
					panic(fmt.Sprintf("Domain pattern '%s' contains invalid character '%c' in label '%s'", pattern, ch, part))
				}
			}
			m.parts[i] = normalized
		}
	}

	// Check if the domain pattern has too many parameters
	if len(m.paramNames) > maxParams {
		panic(fmt.Sprintf("Domain pattern '%s' has %d parameters, which exceeds the maximum of %d",
			pattern, len(m.paramNames), maxParams))
	}

	return m
}

// match checks if a hostname matches the domain pattern.
// It returns true if matched and a slice of parameter values (parallel to paramNames).
// Uses a stack-allocated buffer to avoid heap allocation for typical domain names.

View on GitHub (pinned to a105acad6c)