Skip to content

ERR_INVALID_RETURN_PROPERTY_VALUE with load hook and commonjs #50435

@anomiex

Description

@anomiex

Version

v20.9.0

Platform

Linux abulia 6.5.0-2-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.6-1 (2023-10-07) x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

  1. Create the following files:

    register-hooks.mjs
    import { register } from 'node:module';
    
    register( './hooks.mjs', import.meta.url );
    
    hooks.mjs
    import { readFileSync } from 'node:fs';
    
    export async function load(url, context, nextLoad) {
        if ( context.format !== 'commonjs' && context.format !== 'module' ) {
            return nextLoad(url, context);
        }
    
        // This might in practice be a check for `node_modules`.
        if ( url.endsWith( '/bar.cjs' ) ) {
            return nextLoad(url, context);
        }
    
        // Do some transformation of the source here.
        const source = readFileSync(new URL(url));
    
        return {
            format: context.format,
            source,
            shortCircuit: true,
        };
    } 
    
    foo.cjs
    const bar = require( './bar.cjs' );
    
    bar.hello();
    
    bar.cjs
    function hello() {
        console.log( "hello, world" );
    }
    
    module.exports = {
        hello
    };
    
  2. Run node --import ./register-hooks.mjs foo.cjs

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

Always.

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

Script runs, printing "hello, world".

Because that's what this very simple code does. 🤷

What do you see instead?

node:internal/errors:497
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_INVALID_RETURN_PROPERTY_VALUE]: Expected array buffer, or typed array to be returned for the "source" from the "transformSource" function but got null.
    at new NodeError (node:internal/errors:406:5)
    at assertBufferSource (node:internal/modules/esm/translators:84:9)
    at stringify (node:internal/modules/esm/translators:94:3)
    at createCJSModuleWrap (node:internal/modules/esm/translators:219:12)
    at ModuleLoader.<anonymous> (node:internal/modules/esm/translators:267:10)
    at callTranslator (node:internal/modules/esm/loader:273:14)
    at ModuleLoader.<anonymous> (node:internal/modules/esm/loader:277:24)
    at new ModuleJob (node:internal/modules/esm/module_job:65:26)
    at #createModuleJob (node:internal/modules/esm/loader:290:17)
    at ModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:248:34) {
  code: 'ERR_INVALID_RETURN_PROPERTY_VALUE'
}

Node.js v20.9.0

Additional information

This seems to have been introduced in v20.6.0, as v20.5.1 is not affected. Likely it's part of the enhancements to the load hook added in that version.

What seems to be happening is that once one commonjs file returns a non-null source, any sub-requires of that file will fail if null is returned for source despite that being the default behavior for the passed-in nextLoad function.

I ran into this while trying to figure out why playwright started giving me this error when I tried to update from Node 18 to Node 20. The loader can be found here, and the bug was being triggered by a mjs config file that imports a cjs file that requires something out of node_modules. They've also got some other stuff going on in there, if I hack that loader to always fill in source then it winds up hanging eventually at this line (it seems that sends a message on the MessagePort that the other side never reacts to?).

Considering the comment I see in the doc that "This behavior for nullish source is temporary — in the future, nullish source will not be supported", I suppose you might decide this isn't a bug, saying that any loader that "opts in" to this behavior should carry it all the way through.

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