valyala/fasthttp · error

invalid host %q with multiple port delimiters

Error message

invalid host %q with multiple port delimiters

What it means

url.Parse's parseHost validates the host portion of an authority. After locating the last ':' (candidate port delimiter), it rejects the host if another ':' appears before it. Bracketed IPv6 hosts are handled earlier, so any remaining multi-colon host is invalid.

Source

Thrown at uri.go:473

			}
			host2, err := unescape(host[zone:i], encodeZone)
			if err != nil {
				return nil, err
			}
			host3, err := unescape(host[i:], encodeHost)
			if err != nil {
				return nil, err
			}
			return append(host1, append(host2, host3...)...), nil
		}
	} else {
		if bytes.IndexByte(host, '[') >= 0 || bytes.IndexByte(host, ']') >= 0 {
			return nil, fmt.Errorf("invalid host %q", host)
		}

		if i := bytes.LastIndexByte(host, ':'); i != -1 {
			if bytes.IndexByte(host[:i], ':') != -1 {
				return nil, fmt.Errorf("invalid host %q with multiple port delimiters", host)
			}

			colonPort := host[i:]
			if !validOptionalPort(colonPort) {
				return nil, fmt.Errorf("invalid port %q after host", colonPort)
			}
		}
	}

	var err error
	if host, err = unescape(host, encodeHost); err != nil {
		return nil, err
	}
	if err = validateIPv6Literal(host); err != nil {
		return nil, err
	}
	return host, nil
}

View on GitHub (pinned to c96f600972)

Solutions

  1. Wrap IPv6 literals in square brackets: http://[::1]:8080/
  2. Remove the duplicate port delimiter; pass only host:port once
  3. If input comes from config, split and validate host/port separately before building the URL

Example fix

// before
u, err := url.Parse("http://::1:8080/path")
// after
u, err := url.Parse("http://[::1]:8080/path")
Defensive patterns

Strategy: validation

Validate before calling

func validURLHost(raw string) bool {
    u, err := url.Parse(raw)
    return err == nil && u.Host != ""
}
// call before using the URL in the API

Type guard

func isBracketedIPv6(host string) bool {
    return strings.HasPrefix(host, "[") && strings.Contains(host, "]")
}

Try / catch

u, err := url.Parse(rawURL)
if err != nil {
    var ue *url.Error
    if errors.As(err, &ue) && strings.Contains(ue.Err.Error(), "invalid host") {
        // normalize: bracket IPv6 or strip extra ':'
    }
    return fmt.Errorf("bad URL host in %q: %w", rawURL, err)
}

Prevention

When it happens

Trigger: Parsing a URL whose host contains two or more colons outside brackets, e.g. http.Parse("http://host:8080:90/") or "http://::1/" (unbracketed IPv6 literal).

Common situations: IPv6 addresses pasted without [ ] brackets; concatenating host and port strings twice (host+":"+port where host already has a port); proxy strings like host:port accidentally used as a URL host.


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/ae19bdd642015e20. Report an issue: GitHub.