{"record":{"id":"265fea5c70f47b85","repo":"nodejs/node","slug":"variable-expansion-in-this-context-permits-strings","errorCode":null,"errorMessage":"Variable expansion in this context permits strings and lists only, found {expanded.__class__.__name__} at {index}","messagePattern":"Variable expansion in this context permits strings and lists only, found (.+?) at (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":1424,"sourceCode":"        if isinstance(item, dict):\n            # Make a copy of the variables dict so that it won't influence anything\n            # outside of its own scope.\n            ProcessVariablesAndConditionsInDict(item, phase, variables, build_file)\n        elif isinstance(item, list):\n            ProcessVariablesAndConditionsInList(item, phase, variables, build_file)\n        elif isinstance(item, str):\n            expanded = ExpandVariables(item, phase, variables, build_file)\n            if type(expanded) in (str, int):\n                the_list[index] = expanded\n            elif isinstance(expanded, list):\n                the_list[index : index + 1] = expanded\n                index += len(expanded)\n\n                # index now identifies the next item to examine.  Continue right now\n                # without falling into the index increment below.\n                continue\n            else:\n                raise ValueError(\n                    \"Variable expansion in this context permits strings and \"\n                    + \"lists only, found \"\n                    + expanded.__class__.__name__\n                    + \" at \"\n                    + index\n                )\n        elif not isinstance(item, int):\n            raise TypeError(\n                \"Unknown type \" + item.__class__.__name__ + \" at index \" + index\n            )\n        index = index + 1\n\n\ndef BuildTargetsDict(data):\n    \"\"\"Builds a dict mapping fully-qualified target names to their target dicts.\n\n    |data| is a dict mapping loaded build files by pathname relative to the\n    current directory.  Values in |data| are build file contents.  For each","sourceCodeStart":1406,"sourceCodeEnd":1442,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L1406-L1442","documentation":"In ProcessVariablesAndConditionsInList, each list item that is a str is expanded via ExpandVariables. The expansion result must be a str, an int (assigned in place), or a list (spliced in place); any other type raises this ValueError naming the class and the list index. Note the message text says 'strings and lists only' but the code actually accepts int too — the error fires only for genuinely unsupported types like dict, bool, float, None.","triggerScenarios":"A list item is a '<(var)' reference whose value is a dict, bool, or None; a programmatic caller injects a heterogeneous list; expansion of a command yielding something other than text that decodes to a scalar/list.","commonSituations":"A list-valued variable that accidentally contains a dict; using '<(var)' where var holds a bool/None; templating layers injecting non-string scalars.","solutions":["Ensure the referenced variable holds a str, int, or list before referencing it in list position.","Stringify or remove the offending item (the message names its class and index).","If a bool/None leaked from config, coerce it to a string or 1/0 at the source."],"exampleFix":"// before — flag_var holds a bool\n'sources': ['a.c', '<(flag_var)', 'b.c'],\n// after — make flag_var a string\n'sources': ['a.c', '<(flag_var_str)', 'b.c'],","handlingStrategy":"type-guard","validationCode":"for index, item in enumerate(the_list):\n    if isinstance(item, str):\n        expanded = ExpandVariables(item, phase, variables, build_file)\n        assert type(expanded) in (str, int, list), \\\n            f'Item at index {index} expanded to unsupported {type(expanded).__name__}'","typeGuard":"def is_expandable_list_item(expanded) -> bool:\n    return type(expanded) in (str, int) or isinstance(expanded, list)","tryCatchPattern":null,"preventionTips":["Ensure list-context <() expansions reference str/int/list variables only.","Coerce bool/None config values to strings before binding them to list variables.","Audit heterogeneous lists produced by templating layers."],"tags":["gyp","variables","type-check","list","build-config"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}