nodejs/node · error · GypError

Variable %s must expand to a string or list of strings; list

Error message

Variable %s must expand to a string or list of strings; list contains a %s

What it means

When a variable expands to a list, GYP iterates the list and requires each item to be a str or int (unless the reference ends in '/', which permits dict-valued items). If any item is neither — e.g. a nested dict, a float, None — GYP raises this GypError naming the variable, the requirement, and the offending item's class name. The check guards the downstream ProcessVariablesAndConditionsInList call which assumes string-like elements.

Source

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

                #   '>@(_sources!)',
                # ],
                # 'action/': [
                #   '>@(_sources/)',
                # ],
                replacement = []
            else:
                raise GypError("Undefined variable " + contents + " in " + build_file)
        else:
            replacement = variables[contents]

        if isinstance(replacement, bytes) and not isinstance(replacement, str):
            replacement = replacement.decode("utf-8")  # done on Python 3 only
        if isinstance(replacement, list):
            for item in replacement:
                if isinstance(item, bytes) and not isinstance(item, str):
                    item = item.decode("utf-8")  # done on Python 3 only
                if not contents[-1] == "/" and type(item) not in (str, int):
                    raise GypError(
                        "Variable "
                        + contents
                        + " must expand to a string or list of strings; "
                        + "list contains a "
                        + item.__class__.__name__
                    )
            # Run through the list and handle variable expansions in it.  Since
            # the list is guaranteed not to contain dicts, this won't do anything
            # with conditions sections.
            ProcessVariablesAndConditionsInList(
                replacement, phase, variables, build_file
            )
        elif type(replacement) not in (str, int):
            raise GypError(
                "Variable "
                + contents
                + " must expand to a string or list of strings; "
                + "found a "

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Flatten or stringify the offending items so every element of the list is a str or int.
  2. If dicts in the list are intentional, reference the variable with the '/' suffix form that allows them.
  3. Audit the 'variables' dict and any <!() command output feeding the list to find the non-str/int element (the error names the class).

Example fix

// before
'variables': { 'my_list': ['a', {'nested': 1}, 'b'] },
'args': ['<(my_list)'],
// after — keep the list homogeneous in strings
'variables': { 'my_list': ['a', 'b', 'nested'] },
Defensive patterns

Strategy: type-guard

Validate before calling

def check_list_var(name, value):
    if isinstance(value, list):
        bad = [type(x).__name__ for x in value if type(x) not in (str, int)]
        assert not bad, f'{name} list contains non-str/int: {bad}'

Type guard

def is_str_int_list(value) -> bool:
    return isinstance(value, list) and all(type(x) in (str, int) for x in value)

Prevention

When it happens

Trigger: A 'variables' entry is a list containing a dict (e.g. [{'a':1}, 'b']) and is referenced via '<(var)'; a list containing None or a float produced by a <!() command whose stdout was parsed into a heterogeneous list; a programmatic gyp invocation that injects a list of mixed types.

Common situations: Accidentally nesting a dict inside a list-valued variable; a codegen command returning lines that get split into non-string tokens; building the variables dict dynamically from JSON/config that includes nested objects.

Related errors


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