Skip to content

Custom DNS lookup failing on Node 20+, works on Node 18 #55762

Description

@geocine

Version

20.16.0

Platform

Microsoft Windows NT 10.0.22621.0
x64

Subsystem

No response

What steps will reproduce the bug?

const dns = require('dns');
const https = require('https');

// Custom DNS resolution for github.com
const customDNSLookup = (hostname, options, callback) => {
    if (hostname === 'github.com') {
        // Using one of GitHub's IPs as an example
        return callback(null, '20.205.243.166', 4);
    } else {
        return dns.lookup(hostname, options, callback);
    }
};

// Make a test request using native https
async function testConnection() {
    const options = {
        hostname: 'github.com',
        path: '/',
        method: 'GET',
        headers: {
            'Host': 'github.com'
        },
        agent: new https.Agent({
            rejectUnauthorized: false,
            lookup: customDNSLookup
        })
    };

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            console.log('Successfully connected to GitHub');
            console.log('Using IP:', '20.205.243.166');
            console.log('Status:', res.statusCode);
            resolve(res);
        });

        req.on('error', (error) => {
            console.error('Error:', error.message);
            reject(error);
        });

        req.end();
    });
}

testConnection();

Just run this node repro.js

How often does it reproduce? Is there a required condition?

You can repro it on node v20+ it works on node v18

What is the expected behavior? Why is that the expected behavior?

Successfully connected to GitHub
Using IP: 20.205.243.166
Status: 200

This is the expected behavior because we have defined a custom lookup method to resolve to this IP

What do you see instead?

Error: Invalid IP address: undefined
node:internal/process/promises:391
    triggerUncaughtException(err, true /* fromPromise */);
    ^

TypeError [ERR_INVALID_IP_ADDRESS]: Invalid IP address: undefined
    at emitLookup (node:net:1465:17)
    at customDNSLookup (C:\Aivan\PL\repro.js:8:16)
    at emitLookup (node:net:1414:5)
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnectMultiple (node:net:1413:3)
    at node:net:1359:7
    at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)
    at lookupAndConnect (node:net:1358:5)
    at Socket.connect (node:net:1255:5)
    at Object.connect (node:_tls_wrap:1801:13) {
  code: 'ERR_INVALID_IP_ADDRESS'
}

Node.js v20.16.0

Additional information

I am testing on the same machine, same code above. Just different nodejs

Activity

  1. aduh95 commented on Nov 7, 2024

    @aduh95
    Contributor

    Did you report this to axios? Can you reproduce without external deps?

  2. geocine commented on Nov 7, 2024

    @geocine
    Author

    Did you report this to axios? Can you reproduce without external deps?

    Yes I can repro I updated the code and removed axios

  3. AlphaAzure-Bdeo commented on Nov 7, 2024

    @AlphaAzure-Bdeo

    Had a similar issue. Apparently Node 20 expects a different output out of the function from Node18. Here is the current one that I have for Node 20 that works:

    dns.lookup = function (domain: string, options: any, callback: any) {
                // Handle all possible function signatures
                if (typeof options === 'function') {
                    callback = options;
                    options = {};
                } else if (typeof options === 'number') {
                    options = { family: options };
                } else if (!options) {
                    options = {};
                }
    
                // Check if we have any custom DNS entries
                const customEntry = domains.find(entry => {
                    // Check if the domain matches exactly
                    return domain === entry.source;
                });
    
                if (customEntry) {
                    if (options.all) {
                        // Return array format when options.all is true
                        callback(null, [{
                            address: customEntry.ip,
                            family: customEntry.family || 4
                        }]);
                    } else {
                        // Return single address format
                        callback(null, customEntry.ip, customEntry.family || 4);
                    }
                    return;
                }
    
                // Fall back to original DNS lookup if no custom entry found
                return dnsLookup.call(this, domain, options, callback);
            };
    
  4. huseyinacacak-janea commented on Nov 20, 2024

    @huseyinacacak-janea
    Contributor

    I’ve reviewed your issue and noticed that the default value of autoSelectFamily for connections has changed starting from Node.js v20. In v20, the default value is now true.

    If you want to run your code in Node.js v20 and maintain the previous behavior, you can use the --no-network-family-autoselection flag when starting Node.js. This will disable the automatic selection of the network family and might help address the issue you’re encountering.

  5. dario-colombo commented on Jan 17, 2025

    @dario-colombo

    node <= 18
    callback(err, address, family);
    node >18
    callback(err, [{address, family}]);

  6. StefanStojanovic commented on Feb 14, 2025

    @StefanStojanovic
    Contributor

    Closing this since based on the last few comments it seems to be explained. Feel free to reopen if needed.

  7. DongwonTTuna commented on Jun 13, 2025

    @DongwonTTuna

    I fixed it by adding family options to Agent.

    { 
      ..., 
      dispatcher: new Agent(
        { 
           connect: { 
             lookup: _lookup,
             family: 4  // I added this
          } 
        }
      ) 
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions