nodejs/node · error · GypError
rule_sources must not exist in input, target %s rule %s
Error message
rule_sources must not exist in input, target %s rule %s
What it means
GYP itself synthesizes 'rule_sources' for each rule by scanning the target's sources for files matching the rule's extension. Hand-written 'rule_sources' in the input would be overwritten and is therefore rejected outright so authors do not rely on a value gyp will replace.
Source
Thrown at tools/gyp/pylib/gyp/input.py:2750
if rule_extension in rule_extensions:
raise GypError(
(
"extension %s associated with multiple rules, "
+ "target %s rules %s and %s"
)
% (
rule_extension,
target,
rule_extensions[rule_extension]["rule_name"],
rule_name,
)
)
rule_extensions[rule_extension] = rule
# Make sure rule_sources isn't already there. It's going to be
# created below if needed.
if "rule_sources" in rule:
raise GypError(
"rule_sources must not exist in input, target %s rule %s"
% (target, rule_name)
)
rule_sources = []
source_keys = ["sources"]
source_keys.extend(extra_sources_for_rules)
for source_key in source_keys:
for source in target_dict.get(source_key, []):
(_source_root, source_extension) = os.path.splitext(source)
if source_extension.startswith("."):
source_extension = source_extension[1:]
if source_extension == rule_extension:
rule_sources.append(source)
if len(rule_sources) > 0:
rule["rule_sources"] = rule_sources
View on GitHub (pinned to 1b2de5e052)
Solutions
- Remove the 'rule_sources' key from the offending rule in the .gyp/.gypi file.
- If you need to control which sources a rule processes, instead curate the target's 'sources' list or the rule's extension; gyp derives rule_sources from those.
- Re-run gyp to confirm the rule now validates.
Example fix
// before
{ 'rule_name': 'gen', 'extension': 'idl', 'rule_sources': ['a.idl'], ... },
// after
{ 'rule_name': 'gen', 'extension': 'idl', ... }, Defensive patterns
Strategy: validation
Validate before calling
for r in target_dict.get('rules', []):
if 'rule_sources' in r:
raise ValueError(f"rule {r.get('rule_name')} must not preset 'rule_sources'") Type guard
def rules_have_no_rule_sources(target_dict: dict) -> bool:
return all('rule_sources' not in r for r in target_dict.get('rules', [])) Prevention
- Remember rule_sources is an OUTPUT field generated by gyp, never an input.
- Keep hand-authored rule blocks minimal: rule_name, extension, action/process, outputs, message.
- Avoid diffing against generated gyp output.
When it happens
Trigger: A rule dict inside a target's 'rules' list already contains a 'rule_sources' key before validation runs.
Common situations: Migrating from an older gyp layout that exposed rule_sources; manually editing a rule block generated by a tool and leaving the generated key in place; misunderstanding that rule_sources is an output, not an input.
Related errors
- %s must not contain included_files key
- rule {rule_name} exists in duplicate, target {target}
- extension %s associated with multiple rules, target %s rules
- AddFileConfig: file "%s" not in project.
- expected string; got %r
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/ae5ddb378be8ae9b.
Report an issue: GitHub.