nodejs/node · error · Exception

Unable to open file %s

Error message

Unable to open file %s

What it means

analyzer.py's Config.Init raises Exception('Unable to open file ' + config_path) when open(config_path) throws OSError. The analyzer generator reads a JSON config (set via generator_flags['config_path']) describing which files changed and which targets to test; if the path doesn't exist or isn't readable, Init bails. (A separate ValueError handler covers JSON parse errors; this one is strictly the open() failure.)

Source

Thrown at tools/gyp/pylib/gyp/generator/analyzer.py:277

    def __init__(self):
        self.files = []
        self.targets = set()
        self.additional_compile_target_names = set()
        self.test_target_names = set()

    def Init(self, params):
        """Initializes Config. This is a separate method as it raises an exception
        if there is a parse error."""
        generator_flags = params.get("generator_flags", {})
        config_path = generator_flags.get("config_path", None)
        if not config_path:
            return
        try:
            f = open(config_path)
            config = json.load(f)
            f.close()
        except OSError:
            raise Exception("Unable to open file " + config_path)
        except ValueError as e:
            raise Exception("Unable to parse config file " + config_path + str(e))
        if not isinstance(config, dict):
            raise Exception("config_path must be a JSON file containing a dictionary")
        self.files = config.get("files", [])
        self.additional_compile_target_names = set(
            config.get("additional_compile_targets", [])
        )
        self.test_target_names = set(config.get("test_targets", []))


def _WasBuildFileModified(build_file, data, files, toplevel_dir):
    """Returns true if the build file |build_file| is either in |files| or
    one of the files included by |build_file| is in |files|. |toplevel_dir| is
    the root of the source tree."""
    if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files:
        if debug:
            print("gyp file modified", build_file)

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Verify config_path exists and is readable before invoking gyp: `os.path.isfile(config_path)`.
  2. Use an absolute path for config_path to avoid cwd ambiguity.
  3. Ensure the upstream step that produces the analyzer JSON actually ran and wrote to the expected location.
  4. Check file permissions if running under a different user.

Example fix

# before
python gyp -f analyzer --generator_flag=config_path=changed.json
# (changed.json does not exist)

# after
# generate the file first, then pass an absolute path
python write_analyzer_config.py /abs/changed.json
python gyp -f analyzer --generator_flag=config_path=/abs/changed.json
Defensive patterns

Strategy: validation

Validate before calling

import os
def load_analyzer_config(config_path):
    if not config_path or not os.path.isfile(config_path):
        raise SystemExit('analyzer config_path missing or unreadable: %r' % config_path)
    with open(config_path) as f:
        return json.load(f)

Type guard

def config_path_readable(path: str) -> bool:
    import os
    return bool(path) and os.path.isfile(path) and os.access(path, os.R_OK)

Try / catch

try:
    config.Init(params)
except Exception as e:
    if 'Unable to open file' in str(e):
        # regenerate the changed-files JSON, then retry
        write_analyzer_config()
        config.Init(params)

Prevention

When it happens

Trigger: Invoking gyp -f analyzer with --generator-flag=config_path=<missing.json>; passing a relative path that doesn't resolve under gyp's cwd; permission denied on the config file.

Common situations: CI scripts that write the changed-files JSON to a temp path and pass a stale/wrong location; cross-platform path separators; the analyzer config not being generated by an earlier step.

Related errors


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