nodejs/node · error · GypError
%s must be a list
Error message
%s must be a list
What it means
GYP's 'conditions' and 'target_conditions' sections are lists of [cond_expr, true_dict, (false_dict)] triples. EvalCondition first asserts the whole entry is a Python list; if a single entry is a dict, string, or any non-list type, it raises this GypError. The conditions_key in the message tells you whether the failure is in a 'conditions' or 'target_conditions' block.
Source
Thrown at tools/gyp/pylib/gyp/input.py:1116
for index, outstr in enumerate(output):
if IsStrCanonicalInt(outstr):
output[index] = int(outstr)
elif IsStrCanonicalInt(output):
output = int(output)
return output
# The same condition is often evaluated over and over again so it
# makes sense to cache as much as possible between evaluations.
cached_conditions_asts = {}
def EvalCondition(condition, conditions_key, phase, variables, build_file):
"""Returns the dict that should be used or None if the result was
that nothing should be used."""
if not isinstance(condition, list):
raise GypError(conditions_key + " must be a list")
if len(condition) < 2:
# It's possible that condition[0] won't work in which case this
# attempt will raise its own IndexError. That's probably fine.
raise GypError(
conditions_key
+ " "
+ condition[0]
+ " must be at least length 2, not "
+ str(len(condition))
)
i = 0
result = None
while i < len(condition):
cond_expr = condition[i]
true_dict = condition[i + 1]
if not isinstance(true_dict, dict):
raise GypError(View on GitHub (pinned to 1b2de5e052)
Solutions
- Wrap each condition as a list: [cond_expr_string, true_dict] (plus optional false_dict), and ensure the whole 'conditions' value is a list of such lists.
- Validate the .gyp file parses as expected with python -c "import json; json.load(open('file.gyp'))" (GYP uses a JSON-ish parser).
- Check the conditions_key in the message to find the offending block (conditions vs target_conditions).
Example fix
// before
'conditions': {
'OS == "linux"': { 'defines': ['LINUX'] }
},
// after
'conditions': [
['OS == "linux"', { 'defines': ['LINUX'] }]
], Defensive patterns
Strategy: type-guard
Validate before calling
def validate_conditions(conditions, key='conditions'):
assert isinstance(conditions, list), f'{key} must be a list of [expr, dict, ...] entries'
for entry in conditions:
assert isinstance(entry, list), f'{key} entry {entry!r} must be a list' Type guard
def is_conditions_well_formed(conditions) -> bool:
return isinstance(conditions, list) and all(isinstance(e, list) for e in conditions) Prevention
- Always author 'conditions' as a list of lists, never a mapping.
- Lint .gyp files with a structural validator before committing.
- When converting from other build systems, mind the list-of-triples shape.
When it happens
Trigger: Writing "'conditions': { 'os==\"mac\"': {...} }" (a dict) instead of "'conditions': [ ['os==\"mac\"', {...}] ]" (a list of lists); pasting a YAML-style mapping into a JSON-style .gyp file; a programmatic generator emitting conditions as objects.
Common situations: Confusing the conditions-list syntax with a mapping; missing the outer '[' ']'; malformed hand-edits to a .gyp file; converting from another build system's conditional syntax.
Related errors
- %s %s must be followed by a dictionary, not %s
- %s %s must be at least length 2, not %s
- {conditions_key} {cond_expr} has {len(condition) - i} unexpe
- Variable expansion in this context permits str and int only,
- Unknown type {value.__class__.__name__} for {key}
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/5a37ae5c5b6dc717.
Report an issue: GitHub.