caddyserver/caddy · error

unable to parse URL pattern: %w

Error message

unable to parse URL pattern: %w

What it means

Thrown when MatchURLPattern.Provision compiles the configured pattern via urlpattern.New(pattern, baseURL, opts) and the WICG URL Pattern parser rejects it. This is a config-validation error: the pattern string (and optional base_url) must be parseable URLPattern syntax (e.g. `/books/:id`, `https://example.com/books/*`). The wrapped error from the urlpattern library explains the syntax problem.

Source

Thrown at modules/caddyhttp/urlpatternmatcher.go:67

	}
}

// Provision compiles the URL pattern.
func (m *MatchURLPattern) Provision(_ caddy.Context) error {
	input := m.Pattern

	// A relative pattern with no base matches any origin: prefix wildcard
	// protocol, host and port so only the path, search and hash are
	// constrained. The port wildcard is needed because a request Host may
	// carry an explicit port. Host-scoped matching stays opt-in via an
	// absolute pattern or base_url.
	if m.BaseURL == "" && !strings.Contains(input, "://") {
		input = "*://*:*" + input
	}

	p, err := urlpattern.New(input, m.BaseURL, &urlpattern.Options{IgnoreCase: m.IgnoreCase})
	if err != nil {
		return fmt.Errorf("unable to parse URL pattern: %w", err)
	}

	m.compiledPattern = p

	return nil
}

// Match returns true if the request matches the URL pattern.
func (m *MatchURLPattern) Match(r *http.Request) bool {
	ok, _ := m.MatchWithError(r)

	return ok
}

// MatchWithError returns true if the request matches the URL pattern. The
// request's origin (scheme://host) is the base against which the path is
// resolved, so an absolute pattern or base_url can match on scheme and host.
//

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Read the wrapped urlpattern error — it pinpoints the offending segment of the pattern
  2. Make relative patterns path-absolute: start with `/` (e.g. `/books/:id`, `/search?q=*`)
  3. Use `:name` for named segments and `*` only as a full-component wildcard (`/files/*`), not inside a component
  4. If pattern is absolute (`scheme://host/...`), drop base_url, or set base_url to a full origin like `https://example.com`
  5. Test the pair quickly: urlpattern.New accepts the same string as JavaScript's URLPattern, so try it in a browser console first

Example fix

# before
@url_pattern {
    pattern "books/:id"
    base_url "example.com"
}

# after
@url_pattern {
    pattern "/books/:id"
    base_url "https://example.com"
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate patterns before deploying config (URLPattern mirrors JS URLPattern):
func validPattern(pattern, base string) error {
    in := pattern
    if base == "" && !strings.Contains(in, "://") {
        in = "*://*:*" + in // mirror Caddy's relative-pattern expansion
    }
    _, err := urlpattern.New(in, base, nil)
    return err
}

Try / catch

// urlpattern.New returns (nil, err) rather than panicking; always check both:
p, err := urlpattern.New(pattern, baseURL, nil)
if err != nil {
    return fmt.Errorf("pattern %q rejected: %w", pattern, err)
}
use(p)

Prevention

When it happens

Trigger: A matcher block `@url_pattern pattern /books/{` or expression `url_pattern('/:id')` where the pattern has unmatched/invalid group syntax, a malformed base_url (e.g. `example.com` without scheme when the pattern needs one), a wildcard `*` placed where a full component is required, or invalid port/name characters. Fails at provision/validate time, before any request is matched.

Common situations: Confusing URLPattern syntax with regex or with nginx location syntax; forgetting that a relative pattern is auto-prefixed with `*://*:*` so the pattern must start with `/`; supplying a base_url with a path when the pattern is absolute; using `*` in the middle of a hostname component.

Understand the failure class

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/6c9bc1e73742733f. Report an issue: GitHub.