{"record":{"id":"be2b9681bb7ab6f2","repo":"nodejs/node","slug":"extension-s-associated-with-multiple-rules-targe","errorCode":null,"errorMessage":"extension %s associated with multiple rules, target %s rules %s and %s","messagePattern":"extension (.+?) associated with multiple rules, target (.+?) rules (.+?) and (.+?)","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":2733,"sourceCode":"\n    # Dicts to map between values found in rules' 'rule_name' and 'extension'\n    # keys and the rule dicts themselves.\n    rule_names = {}\n    rule_extensions = {}\n\n    rules = target_dict.get(\"rules\", [])\n    for rule in rules:\n        # Make sure that there's no conflict among rule names and extensions.\n        rule_name = rule[\"rule_name\"]\n        if rule_name in rule_names:\n            raise GypError(f\"rule {rule_name} exists in duplicate, target {target}\")\n        rule_names[rule_name] = rule\n\n        rule_extension = rule[\"extension\"]\n        if rule_extension.startswith(\".\"):\n            rule_extension = rule_extension[1:]\n        if rule_extension in rule_extensions:\n            raise GypError(\n                (\n                    \"extension %s associated with multiple rules, \"\n                    + \"target %s rules %s and %s\"\n                )\n                % (\n                    rule_extension,\n                    target,\n                    rule_extensions[rule_extension][\"rule_name\"],\n                    rule_name,\n                )\n            )\n        rule_extensions[rule_extension] = rule\n\n        # Make sure rule_sources isn't already there.  It's going to be\n        # created below if needed.\n        if \"rule_sources\" in rule:\n            raise GypError(\n                \"rule_sources must not exist in input, target %s rule %s\"","sourceCodeStart":2715,"sourceCodeEnd":2751,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L2715-L2751","documentation":"ValidateRulesInTarget also enforces that each file extension maps to at most one rule per target. After stripping an optional leading dot it records the extension; a second rule claiming the same extension is rejected because GYP would not know which rule to invoke on matching source files.","triggerScenarios":"Two entries in one target's 'rules' list whose 'extension' keys (after a leading '.' is removed) compare equal, e.g. 'foo' and '.foo'.","commonSituations":"Adding a second code-generation rule for the same file type; merging gypi files that each define a rule for the same extension; changing a rule's extension to one already covered by another rule in the same target.","solutions":["Find the two rules whose extension matches (remember the leading dot is optional on both sides).","Decide which single rule should own that extension for this target, or split the source types across separate targets.","Delete or repoint the losing rule's extension to a distinct value, then re-run gyp."],"exampleFix":"// before\n{ 'rule_name': 'rule_a', 'extension': 'foo', ... },\n{ 'rule_name': 'rule_b', 'extension': '.foo', ... },\n// after\n{ 'rule_name': 'rule_a', 'extension': 'foo', ... },\n{ 'rule_name': 'rule_b', 'extension': 'bar', ... },","handlingStrategy":"validation","validationCode":"# Check one extension per rule per target, ignoring leading dots.\nexts = set()\nfor r in target_dict.get('rules', []):\n    ext = r.get('extension', '').lstrip('.')\n    if ext in exts:\n        raise ValueError(f'extension {ext!r} claimed by multiple rules')\n    exts.add(ext)","typeGuard":"def has_unique_rule_extensions(target_dict: dict) -> bool:\n    exts = [r.get('extension', '').lstrip('.') for r in target_dict.get('rules', [])]\n    return len(exts) == len(set(exts))","tryCatchPattern":null,"preventionTips":["Treat file extensions as owned resources: one rule per extension per target.","When splitting codegen across rules, give each a distinct extension.","Document which extensions each rule consumes."],"tags":["gyp","rules","extension","duplicate","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}