{"record":{"id":"54d577b5b14e8263","repo":"nodejs/node","slug":"config-path-must-be-a-json-file-containing-a-dicti","errorCode":null,"errorMessage":"config_path must be a JSON file containing a dictionary","messagePattern":"config_path must be a JSON file containing a dictionary","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/generator/analyzer.py","lineNumber":281,"sourceCode":"        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\n    # First element of included_files is the file itself.\n    if len(data[build_file][\"included_files\"]) <= 1:","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/generator/analyzer.py#L263-L299","documentation":"Thrown by analyzer Config.Init after the config file is parsed as valid JSON but the resulting top-level value is not a Python dict. The analyzer protocol requires the config to be a JSON object with keys like 'files', 'test_targets', and 'additional_compile_targets'. A JSON array, string, number, or bare value at the root is rejected.","triggerScenarios":"The config_path file contains a valid JSON value that is not an object — e.g. `[\"a.cc\"]` (array), `\"a.cc\"` (string), or a number. json.load() succeeds, but `isinstance(config, dict)` is False at analyzer.py:282.","commonSituations":"Writing a bare list of files instead of wrapping it in an object; misunderstanding the analyzer config schema; generating the config with a script that dumps a list rather than a dict.","solutions":["Wrap the top-level value in a JSON object with the required keys, e.g. {\"files\": [...], \"test_targets\": [...], \"additional_compile_targets\": [...]}.","Confirm the file's outermost characters are `{` and `}`, not `[` or a bare literal.","Re-read the analyzer config schema and model your config on a known-good example."],"exampleFix":"// before (config.json)\n[\"src/a.cc\", \"src/b.cc\"]\n// after\n{\n  \"files\": [\"src/a.cc\", \"src/b.cc\"],\n  \"test_targets\": [],\n  \"additional_compile_targets\": []\n}","handlingStrategy":"validation","validationCode":"import json\nwith open('config.json') as f:\n    cfg = json.load(f)\nassert isinstance(cfg, dict), 'config root must be a JSON object'","typeGuard":"def is_config_object(cfg) -> bool:\n    return isinstance(cfg, dict)","tryCatchPattern":"try:\n    config.Init(params)\nexcept Exception as e:\n    if 'must be a JSON file containing a dictionary' in str(e):\n        rewrite_config_as_object(config_path)\n    raise","preventionTips":["Author the config starting from a known-good object template.","Assert the parsed root is a dict in your config-generation script."],"tags":["gyp","analyzer","json","config","schema"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}