gastownhall/beads · error

--addr %q: host must be a numeric IP literal, not a name — u

Error message

--addr %q: host must be a numeric IP literal, not a name — use 127.0.0.1 rather than localhost

What it means

ValidateBindAddr requires the host portion of --addr to be a numeric IP literal. net.ParseIP failed on the host, meaning a DNS name (like 'localhost') was supplied. The library deliberately refuses names and suggests 127.0.0.1.

Source

Thrown at internal/httpapi/server.go:448

// Dolt child already lives under (validateManagedServerConfigPolicy in
// cmd/bd/proxied_server.go): the host must be a NUMERIC IP literal.
//
// Hostnames are refused, "localhost" included. A name is not a listener
// specification — it resolves to whatever the host's resolver says today, so
// the operator cannot tell from the flag which interfaces they just opened.
// Unix sockets are not supported at all; they fail here because they do not
// parse as host:port.
func ValidateBindAddr(addr string, allowNonLoopback bool) (net.IP, error) {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return nil, fmt.Errorf("--addr %q must be HOST:PORT with a numeric IP literal host (unix sockets are not supported): %w", addr, err)
	}
	if _, err := strconv.ParseUint(port, 10, 16); err != nil {
		return nil, fmt.Errorf("--addr %q: port must be a number from 0 to 65535 (0 picks an ephemeral port)", addr)
	}
	ip := net.ParseIP(host)
	if ip == nil {
		return nil, fmt.Errorf("--addr %q: host must be a numeric IP literal, not a name — use 127.0.0.1 rather than localhost", addr)
	}
	if !ip.IsLoopback() && !allowNonLoopback {
		return nil, fmt.Errorf("--addr %q binds beyond loopback, which requires --allow-non-loopback (and, with it, --auth-token-file)", addr)
	}
	return ip, nil
}

// Listen validates the configuration, binds the listener, and reports the
// bound address on stdout and the startup state on stderr. It does not accept
// anything until Serve runs.
//
// There is no lock file, pid file or discovery file: bd serve is
// operator-invoked and the TCP bind IS the mutual exclusion, so a second
// instance on the same fixed port fails here with the operating system's own
// address-in-use error. (Under the ephemeral default that exclusion does not
// exist — N instances simply run on N ports — which is why fixed ports are the
// deployment recommendation.)
func Listen(cfg Config) (*Server, error) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Replace the name with a numeric IP literal, e.g. --addr 127.0.0.1:8080 instead of localhost:8080
  2. Use ::1 or [::1]:PORT for IPv6 loopback
  3. Resolve the hostname to an IP outside the tool if a remote bind is truly needed (then see --allow-non-loopback)

Example fix

// before
--addr localhost:8080
// after
--addr 127.0.0.1:8080
Defensive patterns

Strategy: validation

Validate before calling

func validIPHost(addr string) bool {
    host, _, err := net.SplitHostPort(addr)
    return err == nil && net.ParseIP(host) != nil
}

Try / catch

if ip, err := httpapi.ValidateBindAddr(addr, false); err != nil {
    log.Fatalf("bad --addr: %v", err)
} else { _ = ip }

Prevention

When it happens

Trigger: Calling ValidateBindAddr with an addr whose host is not parseable as an IP: 'localhost:8080', a hostname like 'myhost:8080', or malformed IP text like '127.0.0.999:8080'.

Common situations: Users habitually write 'localhost:PORT' in dev setups, copy a Kubernetes service DNS name into --addr, or typo an IPv4/IPv6 literal.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/c5ad711638d9dacc. Report an issue: GitHub.