{"record":{"id":"df8631ef1ed18565","repo":"nodejs/node","slug":"anonymous-action-in-target-s-an-action-must-hav","errorCode":null,"errorMessage":"Anonymous action in target %s.  An action must have an 'action_name' field.","messagePattern":"Anonymous action in target (.+?)\\.  An action must have an 'action_name' field\\.","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":2812,"sourceCode":"            \"The 'working_directory' for 'run_as' in target %s \"\n            \"in file %s should be a string.\" % (target_name, build_file)\n        )\n    environment = run_as.get(\"environment\")\n    if environment and not isinstance(environment, dict):\n        raise GypError(\n            \"The 'environment' for 'run_as' in target %s \"\n            \"in file %s should be a dictionary.\" % (target_name, build_file)\n        )\n\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)","sourceCodeStart":2794,"sourceCodeEnd":2830,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L2794-L2830","documentation":"ValidateActionsInTarget iterates target_dict['actions'] and requires each action to declare a non-empty 'action_name'. The name is how the build system identifies and de-duplicates the action, so an anonymous (missing or empty) name is rejected.","triggerScenarios":"An entry in a target's 'actions' list where action.get('action_name') is falsy (None, empty string).","commonSituations":"Adding an action and forgetting the action_name field; copy-pasting an action and leaving a blank name; a typo like 'name' instead of 'action_name'.","solutions":["Add a unique, descriptive 'action_name' string to the action dict.","Verify the field is spelled exactly 'action_name'.","Re-run gyp."],"exampleFix":"// before\n{ 'inputs': ['in.txt'], 'action': ['gen', 'out.cc'] },\n// after\n{ 'action_name': 'gen_out', 'inputs': ['in.txt'], 'action': ['gen', 'out.cc'] },","handlingStrategy":"validation","validationCode":"for a in target_dict.get('actions', []):\n    if not a.get('action_name'):\n        raise ValueError('every action needs a non-empty action_name')","typeGuard":"def actions_all_named(target_dict: dict) -> bool:\n    return all(a.get('action_name') for a in target_dict.get('actions', []))","tryCatchPattern":null,"preventionTips":["Make action_name a required field in your .gyp authoring template.","Use a descriptive, target-scoped name to aid debugging.","Lint actions in pre-commit."],"tags":["gyp","actions","required-field","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}