nodejs/node · error · ValueError

{name} key {list_key} must be list, not {value.__class__.__n

Error message

{name} key {list_key} must be list, not {value.__class__.__name__} when applying {"!": "exclusion", "/": "regex"}[operation]

What it means

When a list-filter key ('sources!'/sources/') is itself a list, the BASE list it operates on ('sources') must also be a list. If the_dict[list_key] is present but not a list, gyp raises ValueError naming the base key, its actual type, and the operation (exclusion or regex).

Source

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

        if operation not in {"!", "/"}:
            continue

        if not isinstance(value, list):
            raise ValueError(
                name + " key " + key + " must be list, not " + value.__class__.__name__
            )

        list_key = key[:-1]
        if list_key not in the_dict:
            # This happens when there's a list like "sources!" but no corresponding
            # "sources" list.  Since there's nothing for it to operate on, queue up
            # the "sources!" list for deletion now.
            del_lists.append(key)
            continue

        if not isinstance(the_dict[list_key], list):
            value = the_dict[list_key]
            raise ValueError(
                name
                + " key "
                + list_key
                + " must be list, not "
                + value.__class__.__name__
                + " when applying "
                + {"!": "exclusion", "/": "regex"}[operation]
            )

        if list_key not in lists:
            lists.append(list_key)

    # Delete the lists that are known to be unneeded at this point.
    for del_list in del_lists:
        del the_dict[del_list]

    for list_key in lists:
        the_list = the_dict[list_key]

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Ensure the base key (the part before '!' or '/') is a list whenever a filter for it exists.
  2. Convert the scalar to a single-element list or remove the filter.
  3. Check merge/include order that may have overwritten the base.

Example fix

// before: { 'sources': 'a.cc', 'sources!': ['a.cc'] }
// after: { 'sources': ['a.cc', 'b.cc'], 'sources!': ['a.cc'] }
Defensive patterns

Strategy: type-guard

Validate before calling

for k, v in the_dict.items():
    if k[-1:] in ('!', '/') and isinstance(v, list):
        base = k[:-1]
        if base in the_dict:
            assert isinstance(the_dict[base], list), '%s base must be list' % base

Type guard

def base_is_list_when_filtered(d, filter_key): return isinstance(d.get(filter_key[:-1]), list)

Try / catch

try:
    gyp.main(args)
except ValueError as e:
    if 'must be list' in str(e) and 'when applying' in str(e): print('Make the base list a real list'); raise

Prevention

When it happens

Trigger: the_dict contains both a filter key (e.g. 'sources!') that is a valid list AND a base key 'sources' that is NOT a list (e.g. a string or None). Reached after the filter-key type check passes.

Common situations: 'sources' accidentally set to a string while 'sources!' is a list; configuration merge that converted the base list to a scalar; refactoring that left a stale scalar base.

Related errors


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