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
- Bind the IPv4 default address only once per endpoint; remove duplicate bind_addr_v4 calls.
- Bind a specific address instead of the default (0.0.0.0) for the second transport.
- Inspect endpoint.transports (or your builder config) before binding to detect an existing user-defined IPv4 default.
- 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
- Call bind_addr_v4 at most once per endpoint
- Track bound defaults in your config layer before constructing the endpoint
- Use specific addresses for secondary transports
- Guard builder calls with a check for existing user-defined default transports
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
- Invalid transport configuration
- The relay is rate-limiting this endpoint; outbound relay…
- NoObservedAddr
- UnexpectedUpgradeStatus
- ExceedsMaxPacketSize
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)