denoland/deno · error

invalid IPv4 address

Error message

invalid IPv4 address

What it means

Thrown by Deno's node:dgram implementation of socket.setMulticastInterface (op_node_udp_set_multicast_interface, ext/node/ops/udp.rs). When the socket is IPv4 (is_ipv6=false), the interface argument is parsed with Rust's Ipv4Addr::parse; any value that is not a literal dotted-quad IPv4 address fails with ErrorKind::InvalidInput and this message. This mirrors libuv, which returns EINVAL for an unparseable multicast interface address.

Source

Thrown at ext/node/ops/udp.rs:375

  sock_ref.set_ttl(ttl)?;
  Ok(())
}

#[op2(fast)]
pub fn op_node_udp_set_multicast_interface(
  state: &mut OpState,
  #[smi] rid: ResourceId,
  is_ipv6: bool,
  #[string] interface_address: &str,
) -> Result<(), NodeUdpError> {
  let resource = state.resource_table.get::<NodeUdpSocketResource>(rid)?;
  let sock_ref = socket2::SockRef::from(&resource.socket);
  if is_ipv6 {
    let index = ipv6_interface_index(interface_address)?;
    sock_ref.set_multicast_if_v6(index)?;
  } else {
    let addr: Ipv4Addr = interface_address.parse().map_err(|_| {
      NodeUdpError::Io(std::io::Error::new(
        std::io::ErrorKind::InvalidInput,
        "invalid IPv4 address",
      ))
    })?;
    sock_ref.set_multicast_if_v4(&addr)?;
  }
  Ok(())
}

/// Parse an IPv6 interface address string to a network interface index.
/// Matches libuv's `uv__udp_set_multicast_interface6` behavior:
/// - Parses IPv6 address strings like `::%lo0`, `::%1`, `::`
/// - Extracts scope_id and resolves interface names via if_nametoindex
/// - Returns EINVAL for empty or unparseable addresses
fn ipv6_interface_index(interface_address: &str) -> Result<u32, NodeUdpError> {
  let einval =
    || NodeUdpError::Io(std::io::Error::from_raw_os_error(libc::EINVAL));

View on GitHub (pinned to a961cdec3b)

Solutions

  1. Pass a valid IPv4 literal matching the socket family: '0.0.0.0' for the default interface, or the address of a NIC from os.networkInterfaces() where family === 'IPv4'.
  2. If the socket is IPv6, pass an IPv6 literal with optional zone ('::' or 'fe80::1%eth0'); do not mix families between bind() and setMulticastInterface().
  3. Validate the value with node:net isIPv4()/isIPv6() before calling the API and fail fast with the offending value in the message.
  4. If the value comes from config, log it when EINVAL is caught so the bad input is visible.

Example fix

// before
sock.setMulticastInterface(process.env.MULTICAST_IFACE ?? "eth0"); // EINVAL: invalid IPv4 address

// after
import { isIPv4, isIPv6 } from "node:net";
import { networkInterfaces } from "node:os";
const anyV4 = Object.values(networkInterfaces()).flat().find(a => a?.family === "IPv4")?.address ?? "0.0.0.0";
const iface = process.env.MULTICAST_IFACE ?? anyV4;
if (sock.address().family === "IPv4" && !isIPv4(iface)) throw new Error(`MULTICAST_IFACE must be IPv4, got ${iface}`);
if (sock.address().family === "IPv6" && !isIPv6(iface)) throw new Error(`MULTICAST_IFACE must be IPv6, got ${iface}`);
sock.setMulticastInterface(iface);
Defensive patterns

Strategy: validation

Validate before calling

import { isIPv4 } from "node:net";
const iface = process.env.MULTICAST_IFACE ?? "0.0.0.0";
if (!isIPv4(iface)) throw new Error(`MULTICAST_IFACE must be an IPv4 literal, got: ${iface}`);
sock.setMulticastInterface(iface);

Type guard

import { isIPv4 } from "node:net";
const isIPv4Literal = (v: string): v is `${number}.${number}.${number}.${number}` => isIPv4(v);

Try / catch

try { sock.setMulticastInterface(iface); } catch (e) { if (e instanceof Error && /invalid IPv4 address/.test(e.message)) throw new Error(`bad MULTICAST_IFACE: ${JSON.stringify(iface)}`); throw e; }

Prevention

When it happens

Trigger: Calling dgram socket.setMulticastInterface(x) on a socket bound to IPv4 where x is not an IPv4 literal: an IPv6 address ('::1'), an interface name ('eth0'), a hostname ('localhost'), an empty string, or a scoped address ('fe80::1%eth0'). Only dotted-quad literals like '0.0.0.0', '127.0.0.1', or a local NIC address such as '192.168.1.5' are accepted.

Common situations: Config values copied from C examples that use interface names; code ported between IPv4 and IPv6 sockets without changing the interface argument; env-provided MULTICAST_IFACE misconfigured; confusion between 'any' (0.0.0.0) and loopback; passing the result of os.networkInterfaces() fields of the wrong family.

Related errors


AI-assisted analysis of denoland/deno@a961cdec3b (2026-08-20). Data as JSON: /api/errors/04ce1d4f49342956. Report an issue: GitHub.