cloudflare/cloudflared · error
URL %s has invalid escape characters %s
Error message
URL %s has invalid escape characters %s
What it means
validateUrlString percent-decodes the origin URL with url.PathUnescape before parsing. If the string contains a malformed percent-escape (e.g. a lone '%' not followed by two hex digits), PathUnescape fails and this error is returned. Note the message has a bug: on error unescapedUrl is always the empty string, so the second %s prints nothing useful; the offending input is originUrl.
Source
Thrown at validation/validation.go:100
return "", fmt.Errorf("URL should not be empty")
}
if net.ParseIP(originUrl) != nil {
return validateIP("", originUrl, "")
} else if strings.HasPrefix(originUrl, "[") && strings.HasSuffix(originUrl, "]") {
// ParseIP doesn't recoginze [::1]
return validateIP("", originUrl[1:len(originUrl)-1], "")
}
host, port, err := net.SplitHostPort(originUrl)
// user might pass in an ip address like 127.0.0.1
if err == nil && net.ParseIP(host) != nil {
return validateIP("", host, port)
}
unescapedUrl, err := url.PathUnescape(originUrl)
if err != nil {
return "", fmt.Errorf("URL %s has invalid escape characters %s", originUrl, unescapedUrl)
}
parsedUrl, err := url.Parse(unescapedUrl)
if err != nil {
return "", fmt.Errorf("URL %s has invalid format", originUrl)
}
// if the url is in the form of host:port, IsAbs() will think host is the schema
var hostname string
hasScheme := parsedUrl.IsAbs() && parsedUrl.Host != ""
if hasScheme {
err := validateScheme(parsedUrl.Scheme)
if err != nil {
return "", err
}
// The earlier check for ip address will miss the case http://[::1]
// and http://[::1]:8080
if net.ParseIP(parsedUrl.Hostname()) != nil {View on GitHub (pinned to 2253eeeb25)
Solutions
- Fix the URL in your config so every '%' is a valid escape (%XX with hex digits) or encode a literal percent as %25
- Check the value actually passed: log or print the originUrl before calling ValidateUrl, since the error's second %s is always empty
- If the percent is intentional in a query/path, percent-encode it correctly (50%25) before validation
- If you only have a hostname, pass just the hostname instead of a full URL
Example fix
// before origin := "http://service/api?filter=100%" ValidateUrl(origin) // invalid escape characters // after origin := "http://service/api?filter=100%25" ValidateUrl(origin)
Defensive patterns
Strategy: validation
Validate before calling
func validEscapes(s string) bool {
_, err := url.PathUnescape(s)
return err == nil
}
if !validEscapes(origin) { /* reject before ValidateUrl */ } Try / catch
if _, err := validation.ValidateUrl(origin); err != nil {
if strings.Contains(err.Error(), "invalid escape characters") {
// fall back to percent-encoding literal '%'
}
} Prevention
- Percent-encode literal '%' as %25 before passing URLs
- Trim and sanitize config values read from files/env
- Never build URLs by naive string concatenation with unencoded values
When it happens
Trigger: Calling ValidateUrl or NewAccessValidator with a URL containing an invalid percent sequence such as 'http://example.com/100%' or 'localhost%zz:8080'. Only reached when the input is not a bare IP or host:port IP form.
Common situations: Config file or CLI flag where a URL was copied with a raw '%' (e.g. a query string like '?width=50%' or a password containing '%'), or shell/env interpolation left a stray percent; also URLs built by string concatenation with encoded characters.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Hostname(actually a URL) %s has invalid escape characters %s
- URL %s has invalid format
- URL no %s has invalid format
- not a valid host
- no input provided
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/194d168ae7f4ea65.
Report an issue: GitHub.