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

  1. Validate/normalize the port as numeric (1-65535) before parsing
  2. Use net/url.Parse for friendlier upstream validation, or reject the input at the edge
  3. Return 400 for client-supplied malformed targets in server code
  4. 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

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.