{"record":{"id":"5a37ae5c5b6dc717","repo":"nodejs/node","slug":"s-must-be-a-list","errorCode":null,"errorMessage":"%s must be a list","messagePattern":"(.+?) must be a list","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":1116,"sourceCode":"        for index, outstr in enumerate(output):\n            if IsStrCanonicalInt(outstr):\n                output[index] = int(outstr)\n    elif IsStrCanonicalInt(output):\n        output = int(output)\n\n    return output\n\n\n# The same condition is often evaluated over and over again so it\n# makes sense to cache as much as possible between evaluations.\ncached_conditions_asts = {}\n\n\ndef EvalCondition(condition, conditions_key, phase, variables, build_file):\n    \"\"\"Returns the dict that should be used or None if the result was\n    that nothing should be used.\"\"\"\n    if not isinstance(condition, list):\n        raise GypError(conditions_key + \" must be a list\")\n    if len(condition) < 2:\n        # It's possible that condition[0] won't work in which case this\n        # attempt will raise its own IndexError.  That's probably fine.\n        raise GypError(\n            conditions_key\n            + \" \"\n            + condition[0]\n            + \" must be at least length 2, not \"\n            + str(len(condition))\n        )\n\n    i = 0\n    result = None\n    while i < len(condition):\n        cond_expr = condition[i]\n        true_dict = condition[i + 1]\n        if not isinstance(true_dict, dict):\n            raise GypError(","sourceCodeStart":1098,"sourceCodeEnd":1134,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L1098-L1134","documentation":"GYP's 'conditions' and 'target_conditions' sections are lists of [cond_expr, true_dict, (false_dict)] triples. EvalCondition first asserts the whole entry is a Python list; if a single entry is a dict, string, or any non-list type, it raises this GypError. The conditions_key in the message tells you whether the failure is in a 'conditions' or 'target_conditions' block.","triggerScenarios":"Writing \"'conditions': { 'os==\\\"mac\\\"': {...} }\" (a dict) instead of \"'conditions': [ ['os==\\\"mac\\\"', {...}] ]\" (a list of lists); pasting a YAML-style mapping into a JSON-style .gyp file; a programmatic generator emitting conditions as objects.","commonSituations":"Confusing the conditions-list syntax with a mapping; missing the outer '[' ']'; malformed hand-edits to a .gyp file; converting from another build system's conditional syntax.","solutions":["Wrap each condition as a list: [cond_expr_string, true_dict] (plus optional false_dict), and ensure the whole 'conditions' value is a list of such lists.","Validate the .gyp file parses as expected with python -c \"import json; json.load(open('file.gyp'))\" (GYP uses a JSON-ish parser).","Check the conditions_key in the message to find the offending block (conditions vs target_conditions)."],"exampleFix":"// before\n'conditions': {\n  'OS == \"linux\"': { 'defines': ['LINUX'] }\n},\n// after\n'conditions': [\n  ['OS == \"linux\"', { 'defines': ['LINUX'] }]\n],","handlingStrategy":"type-guard","validationCode":"def validate_conditions(conditions, key='conditions'):\n    assert isinstance(conditions, list), f'{key} must be a list of [expr, dict, ...] entries'\n    for entry in conditions:\n        assert isinstance(entry, list), f'{key} entry {entry!r} must be a list'","typeGuard":"def is_conditions_well_formed(conditions) -> bool:\n    return isinstance(conditions, list) and all(isinstance(e, list) for e in conditions)","tryCatchPattern":null,"preventionTips":["Always author 'conditions' as a list of lists, never a mapping.","Lint .gyp files with a structural validator before committing.","When converting from other build systems, mind the list-of-triples shape."],"tags":["gyp","conditions","syntax","build-config","type-check"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}