denoland/deno · error · Error

Invalid IP address: ${addr0}

Error message

Invalid IP address: ${addr0}

What it means

ChannelWrap's lookup-like methods accept a primary local address (addr0) that must be a valid IPv4 or IPv6 literal. The address is validated with isIPv4/isIPv6 before being stored in #localAddress for use with c-ares name resolution. If addr0 parses as neither, this error is thrown to prevent passing a malformed binding address to the resolver.

Source

Thrown at ext/node/polyfills/internal_binding/cares_wrap.ts:926

  setLocalAddress(addr0: string, addr1?: string) {
    // Mirror Node's c-ares `ChannelWrap::SetLocalAddress`: the first argument
    // may be either an IPv4 or IPv6 address; if a second argument is given it
    // must be the *other* family (so exactly one IPv4 and one IPv6 address, in
    // either order). The caller (`Resolver.setLocalAddress` in
    // `internal/dns/utils.ts`) has already validated the arguments are strings.
    //
    // We validate the addresses here and record them so the call no longer
    // throws and matches Node's observable behavior. The underlying resolver op
    // (`op_dns_resolve`) does not yet expose a per-query bind address, so the
    // stored value is not applied to outgoing queries; see
    // https://github.com/denoland/deno/issues/36518.
    let type0: 4 | 6;
    if (isIPv4(addr0)) {
      type0 = 4;
    } else if (isIPv6(addr0)) {
      type0 = 6;
    } else {
      throw new Error(`Invalid IP address: ${addr0}`);
    }

    if (addr1 !== undefined) {
      if (isIPv4(addr1)) {
        if (type0 === 4) {
          throw new Error("Cannot specify two IPv4 addresses");
        }
      } else if (isIPv6(addr1)) {
        if (type0 === 6) {
          throw new Error("Cannot specify two IPv6 addresses");
        }
      } else {
        throw new Error(`Invalid IP address: ${addr1}`);
      }
    }

    this.#localAddress = {
      ipv4: type0 === 4 ? addr0 : addr1,

View on GitHub (pinned to a961cdec3b)

Solutions

  1. Pass a valid IP literal (e.g. '0.0.0.0', '::') instead of a hostname in the localAddress option.
  2. Validate the string with isIPv4/isIPv6 (or a regex) before passing it to the resolver.
  3. Resolve a hostname to an IP first (dns.lookup) if you only have a name, then pass the resulting literal.

Example fix

// before
const resolver = new dns.Resolver({ localAddress: 'my-host.internal' });
// after
const addresses = await dns.promises.lookup('my-host.internal');
const resolver = new dns.Resolver({ localAddress: addresses[0].address });
Defensive patterns

Strategy: validation

Validate before calling

function isValidIp(addr) {
  const v4 = /^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$/;
  const v6 = /^[0-9a-fA-F:]+$/; // plus a strict isIPv6 check
  return v4.test(addr) || isIPv6(addr);
}
if (!isValidIp(addr0)) throw new Error(`Refusing to bind local address: ${addr0}`);

Type guard

function isIpLiteral(addr) {
  return typeof addr === 'string' && (isIPv4(addr) || isIPv6(addr));
}

Try / catch

try {
  channel.lookup(...);
} catch (err) {
  if (String(err.message).startsWith('Invalid IP address')) {
    // fall back to no localAddress binding
  }
}

Prevention

When it happens

Trigger: Calling ChannelWrap methods that accept a local address (e.g. getAddrInfo/lookup via the Resolver with a localAddress option) where addr0 is not a valid IPv4 or IPv6 literal — e.g. a hostname like 'localhost', an empty string, or a typo like '192.168.1.256'.

Common situations: Passing a hostname instead of an IP literal in dns.Resolver localAddress/localPort options; reading the address from config or an env var that contains a name rather than a literal; IPv6 written with a zone index or invalid shorthand that fails strict parsing.

Related errors


AI-assisted analysis of denoland/deno@a961cdec3b (2026-09-03). Data as JSON: /api/errors/41785ffa116d6354. Report an issue: GitHub.