valyala/fasthttp · error
invalid port %q after host
Error message
invalid port %q after host
What it means
When parsing the host portion of a URI, parseHost checks that any port following a bracketed IPv6 literal (text after the last ']') is a valid optional port via validOptionalPort (either empty or ':' + digits). If not, it returns "invalid port %q after host". This guards URIs like "[::1]:abc".
Source
Thrown at uri.go:441
}
// parseHost parses host as an authority without user
// information. That is, as host[:port].
//
// Based on https://github.com/golang/go/blob/8ac5cbe05d61df0a7a7c9a38ff33305d4dcfea32/src/net/url/url.go#L619
//
// The host is parsed and unescaped in place overwriting the contents of the host parameter.
func parseHost(host []byte) ([]byte, error) {
if len(host) > 0 && host[0] == '[' {
// Parse an IP-Literal in RFC 3986 and RFC 6874.
// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
i := bytes.LastIndexByte(host, ']')
if i < 0 {
return nil, errors.New("missing ']' in host")
}
colonPort := host[i+1:]
if !validOptionalPort(colonPort) {
return nil, fmt.Errorf("invalid port %q after host", colonPort)
}
// RFC 6874 defines that %25 (%-encoded percent) introduces
// the zone identifier, and the zone identifier can use basically
// any %-encoding it likes. That's different from the host, which
// can only %-encode non-ASCII bytes.
// We do impose some restrictions on the zone, to avoid stupidity
// like newlines.
zone := bytes.Index(host[:i], []byte("%25"))
if zone >= 0 {
host1, err := unescape(host[:zone], encodeHost)
if err != nil {
return nil, err
}
host2, err := unescape(host[zone:i], encodeZone)
if err != nil {
return nil, err
}View on GitHub (pinned to c96f600972)
Solutions
- Validate/normalize the port as numeric (1-65535) before parsing
- Use net/url.Parse for friendlier upstream validation, or reject the input at the edge
- Return 400 for client-supplied malformed targets in server code
- Strip or correct the port and re-attempt the parse if a default port applies
Example fix
// before
var u uri.URI
u.Parse(nil, nil, []byte("http://[::1]:8o80/")) // invalid port "o80"
// after
port := 8_080 // normalize to numeric
var u uri.URI
u.Parse(nil, nil, []byte(fmt.Sprintf("http://[::1]:%d/", port))) Defensive patterns
Strategy: validation
Validate before calling
func portSuffixValid(host string) bool {
if i := strings.LastIndexByte(host, ']'); i >= 0 {
p := host[i+1:]
if p == "" { return true }
if p[0] != ':' { return false }
for _, c := range p[1:] {
if c < '0' || c > '9' { return false }
}
}
return true
} Try / catch
var u uri.URI
if err := u.Parse(nil, nil, raw); err != nil {
if strings.Contains(err.Error(), "invalid port") {
return fmt.Errorf("bad port in target: %w", err) // reject with 400
}
return err
} Prevention
- Validate ports are integers in 1-65535 before constructing URLs
- Beware locale/OCR-corrupted digits (e.g. 'o' vs '0') in copied URLs
- Test IPv6-literal targets with ports in your URL handling tests
When it happens
Trigger: URI.Parse on URIs such as "http://[::1]:8o80/", "//[::1]:x/", or any host in bracket form whose suffix after ']' is not ':' followed by digits only.
Common situations: Proxies forwarding malformed targets from clients; log/URL reconstruction tools that truncate or corrupt ports; hand-assembled URLs with typos in the port.
Related errors
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/6346148c02a19337.
Report an issue: GitHub.