{"record":{"id":"4aca1a80e8d727ea","repo":"nodejs/node","slug":"empty-action-as-command-in-target-s","errorCode":null,"errorMessage":"Empty action as command in target %s.","messagePattern":"Empty action as command in target (.+?)\\.","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":2821,"sourceCode":"\n\ndef ValidateActionsInTarget(target, target_dict, build_file):\n    \"\"\"Validates the inputs to the actions in a target.\"\"\"\n    target_name = target_dict.get(\"target_name\")\n    actions = target_dict.get(\"actions\", [])\n    for action in actions:\n        action_name = action.get(\"action_name\")\n        if not action_name:\n            raise GypError(\n                \"Anonymous action in target %s.  \"\n                \"An action must have an 'action_name' field.\" % target_name\n            )\n        inputs = action.get(\"inputs\", None)\n        if inputs is None:\n            raise GypError(\"Action in target %s has no inputs.\" % target_name)\n        action_command = action.get(\"action\")\n        if action_command and not action_command[0]:\n            raise GypError(\"Empty action as command in target %s.\" % target_name)\n\n\ndef TurnIntIntoStrInDict(the_dict):\n    \"\"\"Given dict the_dict, recursively converts all integers into strings.\"\"\"\n    # Use items instead of iteritems because there's no need to try to look at\n    # reinserted keys and their associated values.\n    for k, v in the_dict.items():\n        if isinstance(v, int):\n            v = str(v)\n            the_dict[k] = v\n        elif isinstance(v, dict):\n            TurnIntIntoStrInDict(v)\n        elif isinstance(v, list):\n            TurnIntIntoStrInList(v)\n\n        if isinstance(k, int):\n            del the_dict[k]\n            the_dict[str(k)] = v","sourceCodeStart":2803,"sourceCodeEnd":2839,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L2803-L2839","documentation":"ValidateActionsInTarget checks that, when an 'action' command list is present, its first element (the executable) is non-empty. An empty program name would produce an unrunnable build step, so it is rejected up front.","triggerScenarios":"action.get('action') is truthy (a non-empty list) but the first element action['action'][0] is falsy, typically an empty string.","commonSituations":"A variable expansion that resolved to an empty program name; an action list that starts with '' by mistake; malformed argv after editing.","solutions":["Inspect the first element of the action command list and ensure it is the real executable path or command.","Fix any variable expansion that produced an empty string.","Re-run gyp."],"exampleFix":"// before\n{ 'action_name': 'gen', 'inputs': ['in'], 'action': ['', 'out.cc'] },\n// after\n{ 'action_name': 'gen', 'inputs': ['in'], 'action': ['gen', 'out.cc'] },","handlingStrategy":"validation","validationCode":"for a in target_dict.get('actions', []):\n    cmd = a.get('action')\n    if cmd and not cmd[0]:\n        raise ValueError(f\"action {a.get('action_name')} has an empty command\")","typeGuard":"def actions_have_nonempty_command(target_dict: dict) -> bool:\n    for a in target_dict.get('actions', []):\n        cmd = a.get('action')\n        if cmd and not cmd[0]:\n            return False\n    return True","tryCatchPattern":null,"preventionTips":["Resolve gyp variables before trusting a command; an empty expansion means a missing variable.","Always sanity-check the first argv element is a real executable."],"tags":["gyp","actions","command","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}