gastownhall/beads · error
--allowed-host %q carries a port; the port is stripped from
Error message
--allowed-host %q carries a port; the port is stripped from a request's Host before matching, so an entry with one could never match
What it means
ValidateAllowedHost rejects entries carrying a port (a ':' after the host, excluding bracketed IPv6). The server strips the port from a request's Host header before matching, so an allow-list entry with a port could never match — the flag refuses it rather than silently never matching.
Source
Thrown at internal/httpapi/server.go:1875
func ValidateAllowedHost(v string) error {
if strings.TrimSpace(v) == "" {
return errors.New("--allowed-host is empty; pass the Host header value clients send, such as bd-myproject.beads.svc.cluster.local")
}
if strings.ContainsAny(v, " \t\r\n") {
return fmt.Errorf("--allowed-host %q contains whitespace; it must be a bare host name or IP", v)
}
if strings.ContainsAny(v, "/@") {
return fmt.Errorf("--allowed-host %q looks like a URL; pass just the host, with no scheme and no path", v)
}
// An IPv6 address is spelled in brackets in a Host header, so an operator
// copying one off the wire types it that way. hostOnly strips them before
// matching, so the entry works; refusing it here — with a message about a
// port it does not have — would be the validation lying about the policy.
if net.ParseIP(strings.TrimSuffix(strings.TrimPrefix(v, "["), "]")) != nil {
return nil
}
if strings.Contains(v, ":") {
return fmt.Errorf("--allowed-host %q carries a port; the port is stripped from a request's Host before matching, so an entry with one could never match", v)
}
return nil
}
// allows reports whether a Host header value is one this server answers to.
func (p hostPolicy) allows(host string) bool {
h := hostOnly(host)
if p.names[h] {
return true
}
ip := net.ParseIP(h)
if ip == nil {
return false
}
return p.anyIP || containsIP(p.ips, ip)
}
// label renders the policy for the startup line, so an operator can read whatView on GitHub (pinned to 71377f2769)
Solutions
- Remove the port and pass only the host: --allowed-host bd.example.com
- For IPv6 loopback use bracket form: --allowed-host "[::1]"
- Rely on the client to send Host with or without port; matching ignores the port
Example fix
// before --allowed-host bd.example.com:8080 // after --allowed-host bd.example.com
Defensive patterns
Strategy: validation
Validate before calling
if i := strings.LastIndex(v, ":"); i >= 0 && !strings.HasSuffix(v, "]") {
v = strings.Trim(v[:i], "[]") // strip port for bracketed IPv6
} Try / catch
if err := httpapi.ValidateAllowedHost(v); err != nil {
log.Fatalf("invalid --allowed-host: %v", err)
} Prevention
- Omit the port from allowed-host entries — matching is port-agnostic
- Use bracketed [::1] form for IPv6
- Remember the Host header port is stripped before matching
When it happens
Trigger: Calling ValidateAllowedHost with "bd.example.com:8080" or "example.com:443" (non-IP values containing ':'). Bracketed IPv6 like [::1] is accepted because ParseIP succeeds first.
Common situations: Operators copy the host:port they use in the client's base URL into --allowed-host without realizing matching is port-agnostic.
Related errors
- --allowed-host %q contains whitespace; it must be a bare hos
- --allowed-host %q looks like a URL; pass just the host, with
- got %d close reasons for %d issue IDs; provide exactly one s
- cannot specify both --reason-file and --reason/--resolution/
- --reason-file %q is empty; close reason is required
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c31cee34745669ac.
Report an issue: GitHub.