nodejs/node · error · GypError

%s %s must be followed by a dictionary, not %s

Error message

%s %s must be followed by a dictionary, not %s

What it means

In a condition triple [cond_expr, true_dict, (false_dict)], the element immediately after cond_expr must be a dict — the set of keys merged into the enclosing dict when the condition holds. If that second element is a list, string, or any non-dict, EvalCondition raises this GypError naming conditions_key, the cond_expr, and the offending Python type. The false_dict (third element) is optional and only consumed if it too is a dict.

Source

Thrown at tools/gyp/pylib/gyp/input.py:1134

        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(
                f"{conditions_key} {cond_expr} must be followed by a dictionary, "
                f"not {type(true_dict)}"
            )
        if len(condition) > i + 2 and isinstance(condition[i + 2], dict):
            false_dict = condition[i + 2]
            i = i + 3
            if i != len(condition):
                raise GypError(
                    f"{conditions_key} {cond_expr} has "
                    f"{len(condition) - i} unexpected trailing items"
                )
        else:
            false_dict = None
            i = i + 2
        if result is None:
            result = EvalSingleCondition(
                cond_expr, true_dict, false_dict, phase, variables, build_file
            )

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Make the second element a dict, e.g. { 'defines': ['IS_MAC'], 'sources': [...] }.
  2. Move list-valued content (defines, sources) inside that dict under the appropriate key.
  3. If you intended a nested condition, place another 'conditions' key inside the true_dict.

Example fix

// before
'conditions': [ ['OS == "mac"', ['IS_MAC']] ],
// after
'conditions': [ ['OS == "mac"', { 'defines': ['IS_MAC'] }] ],
Defensive patterns

Strategy: type-guard

Validate before calling

def validate_condition(entry, key='conditions'):
    assert isinstance(entry, list) and len(entry) >= 2
    assert isinstance(entry[1], dict), \
        f'{key} {entry[0]!r} true branch must be a dict, got {type(entry[1]).__name__}'

Type guard

def true_branch_is_dict(entry) -> bool:
    return isinstance(entry, list) and len(entry) >= 2 and isinstance(entry[1], dict)

Prevention

When it happens

Trigger: Writing "['OS==\"mac\"', ['IS_MAC']]" — a list instead of a dict as the true body; putting a bare string or a second condition expression where the true_dict belongs; nesting condition lists flatly instead of grouping them.

Common situations: Confusing the condition body with a list of defines; misreading the triple structure and supplying [expr, value, value]; refactoring that moves a list into the wrong slot.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/348019137b8ca3af. Report an issue: GitHub.