n0-computer/iroh · error · InvalidSocketAddr

Only a single default address can be set per IP family

Error message

Only a single default address can be set per IP family

What it means

InvalidSocketAddr::DuplicateDefaultAddr from iroh's Endpoint::bind_addr_with_opts. When binding an IPv4 transport, the code checks whether a user-defined IPv4 default transport already exists; if so, adding another default address for the same IP family is rejected. Only one default (0.0.0.0/::) address per family is allowed.

Solutions

  1. Bind the IPv4 default address only once per endpoint; remove duplicate bind_addr_v4 calls.
  2. Bind a specific address instead of the default (0.0.0.0) for the second transport.
  3. Inspect endpoint.transports (or your builder config) before binding to detect an existing user-defined IPv4 default.
  4. If you need multiple listeners, use separate endpoints rather than multiple default transports.

Example fix

// before
endpoint.bind_addr_v4("0.0.0.0:0".parse()?); // second default v4 bind -> error
// after
if !endpoint.has_ipv4_default() {
    endpoint.bind_addr_v4("0.0.0.0:0".parse()?);
}
Defensive patterns

Strategy: validation

Validate before calling

fn can_bind_v4_default(transports: &[TransportConfig]) -> bool {
    !transports.iter().any(|t| t.is_ipv4_default() && t.is_user_defined())
}

Try / catch

match endpoint.bind_addr_v4(addr) {
    Err(e) if e.to_string().contains("default address") => { /* v4 default already bound; skip */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling endpoint.bind_addr_v4 or bind_addr with a default/unspecified IPv4 address (0.0.0.0) when self.transports already contains a user-defined IPv4 default transport (is_ipv4_default() && is_user_defined()).

Common situations: Calling bind_addr_v4 twice on the same endpoint builder; mixing a config-provided default bind with a programmatic default bind; migrating code that previously allowed rebinding defaults.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


AI-assisted analysis of n0-computer/iroh@2b4de030ce (2026-09-08). Data as JSON: /api/errors/e76279d8da989798. Report an issue: GitHub.

Appendix: source

Thrown at iroh/src/endpoint.rs:467

    #[cfg(not(wasm_browser))]
    pub fn bind_addr_with_opts<A>(
        mut self,
        addr: A,
        opts: BindOpts,
    ) -> Result<Self, InvalidSocketAddr>
    where
        A: ToSocketAddr,
        <A as ToSocketAddr>::Err: Into<InvalidSocketAddr>,
    {
        let addr = addr.to_socket_addr().map_err(Into::into)?;
        match addr {
            SocketAddr::V4(addr) => {
                if self
                    .transports
                    .iter()
                    .any(|t| t.is_ipv4_default() && t.is_user_defined())
                {
                    bail!(InvalidSocketAddr::DuplicateDefaultAddr);
                }

                let ip_net = Ipv4Net::new(*addr.ip(), opts.prefix_len())
                    .map_err(|_| e!(InvalidSocketAddr::InvalidPrefixLength))?;
                self.transports.push(TransportConfig::Ip {
                    config: IpConfig::V4 {
                        ip_net,
                        port: addr.port(),
                        is_required: opts.is_required(),
                        is_default: opts.is_default_route(),
                    },
                    is_user_defined: true,
                });
            }
            SocketAddr::V6(addr) => {
                if self
                    .transports
                    .iter()

View on GitHub (pinned to 2b4de030ce)