{"record":{"id":"d44cdffbc7aefc9b","repo":"nodejs/node","slug":"variable-expansion-in-this-context-permits-str-and-d44cdf","errorCode":null,"errorMessage":"Variable expansion in this context permits str and int only, found {expanded.__class__.__name__} for {key}","messagePattern":"Variable expansion in this context permits str and int only, found (.+?) for (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":1323,"sourceCode":"            variables[key] = value\n\n        # Handle the associated variables dict first, so that any variable\n        # references within can be resolved prior to using them as variables.\n        # Pass a copy of the variables dict to avoid having it be tainted.\n        # Otherwise, it would have extra automatics added for everything that\n        # should just be an ordinary variable in this scope.\n        ProcessVariablesAndConditionsInDict(\n            the_dict[\"variables\"], phase, variables, build_file, \"variables\"\n        )\n\n    LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key)\n\n    for key, value in the_dict.items():\n        # Skip \"variables\", which was already processed if present.\n        if key != \"variables\" and isinstance(value, str):\n            expanded = ExpandVariables(value, phase, variables, build_file)\n            if type(expanded) not in (str, int):\n                raise ValueError(\n                    \"Variable expansion in this context permits str and int \"\n                    + \"only, found \"\n                    + expanded.__class__.__name__\n                    + \" for \"\n                    + key\n                )\n            the_dict[key] = expanded\n\n    # Variable expansion may have resulted in changes to automatics.  Reload.\n    # TODO(mark): Optimization: only reload if no changes were made.\n    variables = variables_in.copy()\n    LoadAutomaticVariablesFromDict(variables, the_dict)\n    LoadVariablesFromVariablesDict(variables, the_dict, the_dict_key)\n\n    # Process conditions in this dict.  This is done after variable expansion\n    # so that conditions may take advantage of expanded variables.  For example,\n    # if the_dict contains:\n    #   {'type':       '<(library_type)',","sourceCodeStart":1305,"sourceCodeEnd":1341,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L1305-L1341","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If the value should be a list, change the key's value to a list literal containing the expansion, e.g. 'sources': ['<@(my_list)'].","Drop the '@' marker so the expansion yields a scalar: '<(my_scalar)'.","Confirm the referenced variable actually holds a scalar (str/int) when used in scalar position."],"exampleFix":"// before — list expansion in scalar position\n'type': '<@(list_of_types)',\n// after — use a scalar variable\n'type': '<(the_type)',","handlingStrategy":"type-guard","validationCode":"for key, value in the_dict.items():\n    if key != 'variables' and isinstance(value, str):\n        expanded = ExpandVariables(value, phase, variables, build_file)\n        assert type(expanded) in (str, int), \\\n            f'{key} expanded to {type(expanded).__name__}; expected str or int in scalar position'","typeGuard":"def is_scalar_value(value) -> bool:\n    return type(value) in (str, int)","tryCatchPattern":null,"preventionTips":["Reserve the '@' list-expansion marker for list contexts only.","Bind scalar keys to scalar variables; bind list keys to lists.","Review dict values after renaming variables to ensure types still line up."],"tags":["gyp","variables","type-check","expansion","build-config"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}