joewalnes/websocketd · error

cannot derive redirect address from %q: %w

Error message

cannot derive redirect address from %q: %w

What it means

redirectAddress derives the HTTP→HTTPS redirect listener's address by replacing the port in the main address with net.SplitHostPort. If the configured address has no valid host:port shape, SplitHostPort fails and this wrapped error is returned, preventing a malformed bind that would have killed every listener.

Source

Thrown at main.go:163

			conn.Close()
			return fmt.Errorf("socket %s is already in use by a running server", path)
		}
		if err := os.Remove(path); err != nil {
			return fmt.Errorf("failed to remove stale socket %s: %w", path, err)
		}
	}
	return serve("unix", path, config, log)
}

// redirectAddress returns addr with its port replaced by redirPort. IPv6
// literals must be split with net.SplitHostPort (which understands brackets);
// splitting on the first colon lands inside "[::1]:port" and produced a
// malformed listener address that failed to bind — and, being a listener
// error, killed every other listener too.
func redirectAddress(addr string, redirPort int) (string, error) {
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		return "", fmt.Errorf("cannot derive redirect address from %q: %w", addr, err)
	}
	return net.JoinHostPort(host, strconv.Itoa(redirPort)), nil
}

// redirectLocation builds the redirect server's Location header: the host
// the client itself sent, switched to the canonical scheme and the main
// server's port. Not an open redirect: the target host is the client's own
// Host header, only the port is rewritten.
func redirectLocation(clientHost, listenAddr string, ssl bool) string {
	scheme := "http"
	if ssl {
		scheme = "https"
	}
	host, _, err := net.SplitHostPort(clientHost)
	if err != nil {
		host = clientHost // no port in Host header — use it verbatim
	}
	_, port, err := net.SplitHostPort(listenAddr)

View on GitHub (pinned to 7a8683dc7f)

Solutions

  1. Provide the address in host:port form, bracketing IPv6: --addr=[::1]:8080 not --addr=::1.
  2. Strip any scheme from the address — websocketd wants only host:port.
  3. Check the wrapped SplitHostPort error in the message: 'missing port' means add :port; 'too many colons' means bracket the IPv6 host.
  4. If the address comes from env/config templating, log or echo the resolved value before starting.

Example fix

// before
--addr=::1 --port=8080
// after
--addr=[::1] --port=8080
Defensive patterns

Strategy: validation

Validate before calling

func validListenAddr(addr string) error {
    if strings.Contains(addr, "://") { return fmt.Errorf("strip the scheme from %q", addr) }
    _, _, err := net.SplitHostPort(addr)
    return err
}

Try / catch

if err := start(); err != nil && strings.Contains(err.Error(), "cannot derive redirect address") {
    log.Fatalf("bad --addr: use host:port and bracket IPv6 (e.g. [::1]:8080): %v", err)
}

Prevention

When it happens

Trigger: The --addr/port-derived listener address lacks a port or is otherwise malformed — e.g. passing an IPv6 literal without brackets like `--addr=::1`, a bare hostname with no colon, or a string with multiple unbracketed colons — while a redirect port is configured.

Common situations: Users writing `--addr=::1` instead of `[::1]:port`; empty or truncated address from a config template; copy-pasting an address that already includes a scheme (`http://host:8080`).

Related errors


AI-assisted analysis of joewalnes/websocketd@7a8683dc7f (2026-09-03). Data as JSON: /api/errors/4e84192ca0c98887. Report an issue: GitHub.