node:dns: reject falsy hostnames in lookup() with ERR_INVALID_ARG_VALUE

RO
3mo ago
+133-105

Fixes #37464

Problem

  • dns.lookup("", cb) (and null, undefined, false, 0, NaN) calls back with address: null, family: 4 (or [] with all: true) and prints a DEP0118 DeprecationWarning. dns.promises.lookup("") resolves with the same value.
  • DEP0118 reached end of life in Node v25.0.0 (nodejs/node#58619). Node 25+ throws ERR_INVALID_ARG_VALUE ("The argument 'hostname' must be a non-empty string. Received ''") from dns.lookup, and dns.promises.lookup returns a promise rejected with the same error. Bun reports process.version v26.3.0, so code written against the Node version Bun claims expects the error.
  • Cause: src/js/node/dns.ts still has the legacy path. lookup() and promises.lookup() call invalidHostname() to emit the warning and then report success for a hostname that was never looked up.

Fix

  • Callback dns.lookup: throw ERR_INVALID_ARG_VALUE synchronously when the hostname is falsy. The check stays after callback and option validation, which is the order Node uses, so argument type errors still win over the empty hostname error.
  • dns.promises.lookup: return Promise.reject(ERR_INVALID_ARG_VALUE) for a falsy hostname, again after option validation. Node does this inside createLookupPromise, so the error is a rejection there, not a synchronous throw; a caller doing lookup(host).catch(...) (or util.promisify(dns.lookup)) keeps working the same way in both runtimes.
  • Removed the invalidHostname() DEP0118 helper, which has no remaining callers.
  • Error code, message, and the falsy set ("", null, undefined, false, 0, NaN) match Node v26.3.0 output for the same inputs.
  • Internal callers are unaffected: net defaults an empty host to "localhost" before calling lookup (as Node does), dgram defaults to the loopback address, and the cluster listen path checks address.length > 0 first.
  • Synced test-dns.js, test-dns-lookup.js, and test-c-ares.js where they asserted the removed DEP0118 behavior. The updated files pass under Node v26.3.0.
  • Verified: bun bd test test/js/node/dns/node-dns.test.js -t "falsy hostname" (13 tests; all fail on main, all pass with this change; the rest of the file has the same set of network dependent failures with and without the change).
  • Verified: bun bd test/js/node/test/parallel/test-dns.js, test-dns-lookup.js, test-c-ares.js exit 0 with the change and fail on main with "Missing expected rejection".
  • The branch was merged with current main after dns.lookup moved to the system resolver (#37383, #37319) and re-verified with the steps above; the diff against main is unchanged.

Background

  • DEP0118: the Node deprecation that covered passing a falsy hostname to dns.lookup. While it was active (runtime deprecated since v11), Node treated such a call as a successful lookup of nothing (address: null) and printed a warning once. It reached end of life in v25.0.0, so current Node rejects the argument instead.
  • ERR_INVALID_ARG_VALUE: Node's error for an argument that has the right type but an unacceptable value; ERR_INVALID_ARG_TYPE (already thrown here for truthy non-string hostnames) is the type counterpart. $ERR_INVALID_ARG_VALUE(name, value, reason) is the builtin helper that creates it with Node's message format.
  • Sync throw vs rejection: in Node, argument type checks in dns.promises.lookup run before the promise is created (so they throw), while the falsy hostname check runs inside the promise executor (so it rejects). This change keeps the same split.
Reproduction
import dnsp from "node:dns/promises";
const cap = (p) => p.then((ok) => ({ ok }), (e) => ({ err: { ctor: e.constructor.name, code: e.code } }));
console.log("lookup(''):  ", JSON.stringify(await cap(dnsp.lookup(""))));
console.log("lookup(null):", JSON.stringify(await cap(dnsp.lookup(null))));

Node v26.3.0:

lookup(''):   {"err":{"ctor":"TypeError","code":"ERR_INVALID_ARG_VALUE"}}
lookup(null): {"err":{"ctor":"TypeError","code":"ERR_INVALID_ARG_VALUE"}}

Bun 1.4.0 (before this change):

lookup(''):   {"ok":{"address":null,"family":4}}
lookup(null): {"ok":{"address":null,"family":4}}
DeprecationWarning: The provided hostname "" is not a valid hostname, and is supported in the dns module solely for compatibility.
 code: "DEP0118"

no test proof · iteration 1 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/node/dns/node-dns.test.js