nodejs/node · error · GypError

Error importing pymod_do_mainmodule (%s): %s

Error message

Error importing pymod_do_mainmodule (%s): %s

What it means

The <!pymod_do_main(modulename args) expansion tells GYP to import a Python module and call its DoMain(args) function to produce the replacement text. When __import__(modulename) raises ImportError, GYP wraps it in this GypError naming the offending module and the underlying import error. The module is imported from build_file_dir appended to sys.path, so it must be importable from there.

Source

Thrown at tools/gyp/pylib/gyp/input.py:941

                replacement = ""

                if command_string == "pymod_do_main":
                    # <!pymod_do_main(modulename param eters) loads |modulename| as a
                    # python module and then calls that module's DoMain() function,
                    # passing ["param", "eters"] as a single list argument. For modules
                    # that don't load quickly, this can be faster than
                    # <!(python modulename param eters). Do this in |build_file_dir|.
                    oldwd = os.getcwd()  # Python doesn't like os.open('.'): no fchdir.
                    if build_file_dir:  # build_file_dir may be None (see above).
                        os.chdir(build_file_dir)
                    sys.path.append(os.getcwd())
                    try:
                        parsed_contents = shlex.split(contents)
                        try:
                            py_module = __import__(parsed_contents[0])
                        except ImportError as e:
                            raise GypError(
                                "Error importing pymod_do_main"
                                "module (%s): %s" % (parsed_contents[0], e)
                            )
                        replacement = str(
                            py_module.DoMain(parsed_contents[1:])
                        ).rstrip()
                    finally:
                        sys.path.pop()
                        os.chdir(oldwd)
                    assert replacement is not None
                elif command_string:
                    raise GypError(
                        "Unknown command string '%s' in '%s'."
                        % (command_string, contents)
                    )
                else:
                    # Fix up command with platform specific workarounds.
                    contents = FixupPlatformCommand(contents)

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Confirm the module file exists in the build_file_dir (or on sys.path) and the name matches parsed_contents[0] exactly.
  2. Run 'python -c "import modulename"' from the build_file_dir to reproduce the ImportError and read the underlying message in the GypError text.
  3. Install any missing third-party dependency the helper imports, or vendor it next to the helper.
  4. Fix typos/shlex artifacts in the <!pymod_do_main(...) expression (the first whitespace-delimited token is the module name).

Example fix

// before
'value': '<!pymod_do_main(build_helper get_version) main',
// after — ensure build_helper.py lives next to the .gyp file:
//   <gyp dir>/build_helper.py  exporting  def DoMain(args): ...
'value': '<!pymod_do_main(build_helper get_version) main',
Defensive patterns

Strategy: validation

Validate before calling

# From the build_file_dir, confirm the helper module imports cleanly before running gyp.
import sys, os
sys.path.insert(0, build_file_dir)
try:
    __import__(module_name)
except ImportError as e:
    raise SystemExit(f"pymod_do_main helper {module_name!r} is not importable: {e}")

Type guard

def is_pymod_importable(module_name: str, build_file_dir: str) -> bool:
    import importlib.util
    spec = importlib.util.find_spec(module_name)
    return spec is not None

Prevention

When it happens

Trigger: A .gyp file uses '<!(pymod_do_main mymodule arg1)' (or the <! shorthand) but 'mymodule' is not on sys.path, has a typo in its name, has a syntax error, or itself imports an unavailable dependency. Also triggers when the module's name parses differently via shlex.split (e.g. stray quotes).

Common situations: Missing PYTHONPATH entry for a helper script shipped alongside the .gyp file; a helper module that was renamed across a refactor; a Python 2/3 incompatibility in the helper; a helper that imports a third-party package not installed in the gyp-running environment (e.g. a v8/node build helper).

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/61899d9d050b036c. Report an issue: GitHub.