{"record":{"id":"4cfdb3dda7492774","repo":"nodejs/node","slug":"unable-to-open-file-s","errorCode":null,"errorMessage":"Unable to open file %s","messagePattern":"Unable to open file (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/generator/analyzer.py","lineNumber":277,"sourceCode":"    def __init__(self):\n        self.files = []\n        self.targets = set()\n        self.additional_compile_target_names = set()\n        self.test_target_names = set()\n\n    def Init(self, params):\n        \"\"\"Initializes Config. This is a separate method as it raises an exception\n        if there is a parse error.\"\"\"\n        generator_flags = params.get(\"generator_flags\", {})\n        config_path = generator_flags.get(\"config_path\", None)\n        if not config_path:\n            return\n        try:\n            f = open(config_path)\n            config = json.load(f)\n            f.close()\n        except OSError:\n            raise Exception(\"Unable to open file \" + config_path)\n        except ValueError as e:\n            raise Exception(\"Unable to parse config file \" + config_path + str(e))\n        if not isinstance(config, dict):\n            raise Exception(\"config_path must be a JSON file containing a dictionary\")\n        self.files = config.get(\"files\", [])\n        self.additional_compile_target_names = set(\n            config.get(\"additional_compile_targets\", [])\n        )\n        self.test_target_names = set(config.get(\"test_targets\", []))\n\n\ndef _WasBuildFileModified(build_file, data, files, toplevel_dir):\n    \"\"\"Returns true if the build file |build_file| is either in |files| or\n    one of the files included by |build_file| is in |files|. |toplevel_dir| is\n    the root of the source tree.\"\"\"\n    if _ToLocalPath(toplevel_dir, _ToGypPath(build_file)) in files:\n        if debug:\n            print(\"gyp file modified\", build_file)","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/generator/analyzer.py#L259-L295","documentation":"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.)","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify config_path exists and is readable before invoking gyp: `os.path.isfile(config_path)`.","Use an absolute path for config_path to avoid cwd ambiguity.","Ensure the upstream step that produces the analyzer JSON actually ran and wrote to the expected location.","Check file permissions if running under a different user."],"exampleFix":"# before\npython gyp -f analyzer --generator_flag=config_path=changed.json\n# (changed.json does not exist)\n\n# after\n# generate the file first, then pass an absolute path\npython write_analyzer_config.py /abs/changed.json\npython gyp -f analyzer --generator_flag=config_path=/abs/changed.json","handlingStrategy":"validation","validationCode":"import os\ndef load_analyzer_config(config_path):\n    if not config_path or not os.path.isfile(config_path):\n        raise SystemExit('analyzer config_path missing or unreadable: %r' % config_path)\n    with open(config_path) as f:\n        return json.load(f)","typeGuard":"def config_path_readable(path: str) -> bool:\n    import os\n    return bool(path) and os.path.isfile(path) and os.access(path, os.R_OK)","tryCatchPattern":"try:\n    config.Init(params)\nexcept Exception as e:\n    if 'Unable to open file' in str(e):\n        # regenerate the changed-files JSON, then retry\n        write_analyzer_config()\n        config.Init(params)","preventionTips":["Use an absolute path for the analyzer config_path generator flag.","Generate the changed-files JSON in a prior build step and assert it exists.","Verify read permissions when running gyp under a service account."],"tags":["gyp","analyzer","config","file-io","json","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}