gastownhall/beads · error

--addr %q must be HOST:PORT with a numeric IP literal host (

Error message

--addr %q must be HOST:PORT with a numeric IP literal host (unix sockets are not supported): %w

What it means

ValidateBindAddr parses the --addr flag and requires the form HOST:PORT where HOST is a numeric IP literal. If net.SplitHostPort fails — because the value is a unix socket path, a bare port, a hostname without port, or malformed bracket syntax — this error is returned. The server deliberately does not support unix sockets.

Source

Thrown at internal/httpapi/server.go:441

	// edge-triggered rather than once per connection.
	maxConns      int
	liveConns     atomic.Int64
	connCapWarned atomic.Bool
}

// ValidateBindAddr enforces the bind posture, following the policy the managed
// 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.
//

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use a numeric IP literal with an explicit port, e.g. --addr 127.0.0.1:8080.
  2. Remove any unix socket path — unix sockets are unsupported; use TCP loopback instead.
  3. Check for missing separators: the value must contain exactly one host:port (use [::1]:8080 for IPv6).
  4. Inspect the wrapped error (%w) — SplitHostPort's message (missing port, too many colons) names the exact malformation.

Example fix

// before
--addr /var/run/bd.sock
// after
--addr 127.0.0.1:8080
Defensive patterns

Strategy: validation

Validate before calling

// run before launching the server
if _, err := net.SplitHostPort(addr); err != nil {
    return fmt.Errorf("--addr must be HOST:PORT with a numeric IP, got %q", addr)
}
host, _, _ := net.SplitHostPort(addr)
if net.ParseIP(host) == nil {
    return fmt.Errorf("--addr host %q must be a numeric IP literal", host)
}

Try / catch

ip, err := httpapi.ValidateBindAddr(addr, allowNonLoopback)
if err != nil {
    // actionable flag error: print usage hint and exit
    fmt.Fprintf(os.Stderr, "invalid --addr: %v\n", err)
    os.Exit(2)
}

Prevention

When it happens

Trigger: Passing --addr values like "/tmp/bd.sock" (unix socket path), "127.0.0.1" (no port), "://", "[::1]" (missing port), or any string that is not host:port to server startup.

Common situations: Operators copying unix-socket configs from other tools (e.g. Docker-style -v socket setups); forgetting the port; pasting a hostname like "localhost:8080" where only a numeric IP is accepted downstream (this specific error fires on the SplitHostPort step); shell quoting stripping a colon.

Related errors


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