nodejs/node · error · ValueError

Variable expansion in this context permits str and int only,

Error message

Variable expansion in this context permits str and int only, found {expanded.__class__.__name__} for {key}

What it means

In ProcessVariablesAndConditionsInDict, every string-valued key in the dict (other than 'variables') is run through ExpandVariables. If expansion yields something other than a str or int (e.g. a list, because a <@(...) list expansion was used where a scalar was expected), GYP raises this ValueError naming the type and the offending key. This guards assignment back into the_dict[key] which must stay scalar.

Source

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

            variables[key] = value

        # Handle the associated variables dict first, so that any variable
        # references within can be resolved prior to using them as variables.
        # Pass a copy of the variables dict to avoid having it be tainted.
        # Otherwise, it would have extra automatics added for everything that
        # should just be an ordinary variable in this scope.
        ProcessVariablesAndConditionsInDict(
            the_dict["variables"], phase, variables, build_file, "variables"
        )

    LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key)

    for key, value in the_dict.items():
        # Skip "variables", which was already processed if present.
        if key != "variables" and isinstance(value, str):
            expanded = ExpandVariables(value, phase, variables, build_file)
            if type(expanded) not in (str, int):
                raise ValueError(
                    "Variable expansion in this context permits str and int "
                    + "only, found "
                    + expanded.__class__.__name__
                    + " for "
                    + key
                )
            the_dict[key] = expanded

    # Variable expansion may have resulted in changes to automatics.  Reload.
    # TODO(mark): Optimization: only reload if no changes were made.
    variables = variables_in.copy()
    LoadAutomaticVariablesFromDict(variables, the_dict)
    LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key)

    # Process conditions in this dict.  This is done after variable expansion
    # so that conditions may take advantage of expanded variables.  For example,
    # if the_dict contains:
    #   {'type':       '<(library_type)',

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. If the value should be a list, change the key's value to a list literal containing the expansion, e.g. 'sources': ['<@(my_list)'].
  2. Drop the '@' marker so the expansion yields a scalar: '<(my_scalar)'.
  3. Confirm the referenced variable actually holds a scalar (str/int) when used in scalar position.

Example fix

// before — list expansion in scalar position
'type': '<@(list_of_types)',
// after — use a scalar variable
'type': '<(the_type)',
Defensive patterns

Strategy: type-guard

Validate before calling

for key, value in the_dict.items():
    if key != 'variables' and isinstance(value, str):
        expanded = ExpandVariables(value, phase, variables, build_file)
        assert type(expanded) in (str, int), \
            f'{key} expanded to {type(expanded).__name__}; expected str or int in scalar position'

Type guard

def is_scalar_value(value) -> bool:
    return type(value) in (str, int)

Prevention

When it happens

Trigger: A dict key is set to a string like '<@(my_list)' (the @ list-expansion form) but is in scalar position (a dict value, not a list element) so expansion returns a list; a '<(var)' reference resolving to a list assigned to a scalar key.

Common situations: Using the '@' list-expansion marker on a key whose value is not a list context; expecting '<(var)' to coerce a list to a string; copying list-context syntax into a dict-value slot.

Related errors


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