{"record":{"id":"57c3d49976db5234","repo":"nodejs/node","slug":"key-s-repeated-at-level-s-with-key-path-s","errorCode":null,"errorMessage":"Key '%s' repeated at level %s with key path '%s'","messagePattern":"Key '(.+?)' repeated at level (.+?) with key path '(.+?)'","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":195,"sourceCode":"    \"\"\"\n\n    syntax_tree = ast.parse(file_contents)\n    assert isinstance(syntax_tree, ast.Module)\n    c1 = syntax_tree.body\n    assert len(c1) == 1\n    c2 = c1[0]\n    assert isinstance(c2, ast.Expr)\n    return CheckNode(c2.value, [])\n\n\ndef CheckNode(node, keypath):\n    if isinstance(node, ast.Dict):\n        dict = {}\n        for key, value in zip(node.keys, node.values):\n            assert isinstance(key, ast.Str)\n            key = key.s\n            if key in dict:\n                raise GypError(\n                    \"Key '\"\n                    + key\n                    + \"' repeated at level \"\n                    + repr(len(keypath) + 1)\n                    + \" with key path '\"\n                    + \".\".join(keypath)\n                    + \"'\"\n                )\n            kp = list(keypath)  # Make a copy of the list for descending this node.\n            kp.append(key)\n            dict[key] = CheckNode(value, kp)\n        return dict\n    elif isinstance(node, ast.List):\n        children = []\n        for index, child in enumerate(node.elts):\n            kp = list(keypath)  # Copy list.\n            kp.append(repr(index))\n            children.append(CheckNode(child, kp))","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L177-L213","documentation":"Raised as a GypError by CheckNode during 'checked' evaluation of a .gyp file when a dictionary literal contains the same key twice. Gyp performs AST-level duplicate-key detection because Python's native dict construction would silently drop the earlier value, hiding a real mistake.","triggerScenarios":"A .gyp/.gypi file evaluated in check mode contains a dict with a repeated key (e.g. two 'sources' entries in the same target). CheckNode tracks seen keys per dict and raises at input.py:189 when a repeat is found.","commonSituations":"Copy-pasting a block and forgetting to remove the original; merging two config snippets that both define the same key; a conditional that, combined with the base, produces a duplicate (though conditions are merged separately, a literal duplicate in one dict is caught).","solutions":["Find the duplicate key reported (the key, nesting level, and key path are in the message) and remove or rename one occurrence.","If two values must coexist, merge them into a single key (e.g. combine two 'sources' lists).","Use the key path from the message to navigate directly to the offending dict."],"exampleFix":"// before\n'targets': [{\n  'target_name': 'foo',\n  'sources': ['a.cc'],\n  'sources': ['b.cc']\n}]\n// after\n'targets': [{\n  'target_name': 'foo',\n  'sources': ['a.cc', 'b.cc']\n}]","handlingStrategy":"validation","validationCode":"import ast\nfor fn in gyp_files:\n    tree = ast.parse(open(fn).read())\n    for node in ast.walk(tree):\n        if isinstance(node, ast.Dict):\n            keys = [k.s for k in node.keys if isinstance(k, ast.Str)]\n            if len(keys) != len(set(keys)):\n                raise ValueError(f'duplicate keys in {fn}')","typeGuard":"import ast\ndef has_no_dup_keys(node) -> bool:\n    if isinstance(node, ast.Dict):\n        keys = [k.s for k in node.keys if isinstance(k, ast.Str)]\n        if len(keys) != len(set(keys)):\n            return False\n    return all(has_no_dup_keys(c) for c in ast.iter_child_nodes(node))","tryCatchPattern":null,"preventionTips":["Lint .gyp files for duplicate dict keys.","Avoid copy-paste that duplicates keys; merge lists instead."],"tags":["gyp","input","duplicate-key","validation","config"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}