{"record":{"id":"de5ea9c2996aaa08","repo":"nodejs/node","slug":"unable-to-parse-config-file-s-s","errorCode":null,"errorMessage":"Unable to parse config file %s%s","messagePattern":"Unable to parse config file (.+?)(.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/generator/analyzer.py","lineNumber":279,"sourceCode":"        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)\n        return True\n","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/generator/analyzer.py#L261-L297","documentation":"Thrown by the analyzer GYP generator's Config.Init when it opens the file pointed to by the config_path generator flag but json.load() raises a ValueError. This means the file exists and is readable, but its contents are not syntactically valid JSON. The exception chains the original parse error so the exact JSON syntax problem is visible.","triggerScenarios":"Running gyp with --format=analyzer and a --generator-flag=config_path=<file> where <file> contains malformed JSON (trailing comma, unquoted keys, single quotes, a comment, or a truncated file). json.load succeeds at open() but fails during parsing, landing in the `except ValueError` branch at analyzer.py:278.","commonSituations":"Hand-editing the analyzer config JSON and introducing a syntax error; generating the config file with a script that emits JS-style object literals (unquoted keys) or trailing commas; a truncated file from an interrupted write; copy-pasting from a Python dict repr instead of valid JSON.","solutions":["Validate the config file with a JSON linter (e.g. `python -m json.tool config.json`) to surface the exact syntax error and line.","Ensure all keys are double-quoted, remove trailing commas, and remove any comments — strict JSON only.","Regenerate the config file programmatically with json.dump() rather than hand-writing or string-formatting it.","Check that the file was fully written (no truncation) and has the correct encoding (UTF-8, no BOM)."],"exampleFix":"// before (config.json)\n{\n  files: ['a.cc'],   // unquoted keys + trailing comma\n}\n// after\n{\n  \"files\": [\"a.cc\"]\n}","handlingStrategy":"validation","validationCode":"import json, sys\npath = 'config.json'\ntry:\n    with open(path) as f:\n        json.load(f)\n    print('config JSON is valid')\nexcept json.JSONDecodeError as e:\n    print(f'invalid JSON: {e}'); sys.exit(1)\nexcept OSError as e:\n    print(f'cannot open: {e}'); sys.exit(1)","typeGuard":"def is_valid_config_dict(obj) -> bool:\n    return isinstance(obj, dict) and 'files' in obj","tryCatchPattern":"try:\n    config.Init(params)\nexcept Exception as e:\n    if 'Unable to parse config file' in str(e):\n        report_config_error(config_path, e)\n    raise","preventionTips":["Validate the analyzer config with python -m json.tool before invoking gyp.","Generate the config file with json.dump rather than hand-writing JSON.","Run a pre-flight check in CI that parses every config_path file."],"tags":["gyp","analyzer","json","config","parse-error"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}