{"record":{"id":"305b580352cd6b14","repo":"nodejs/node","slug":"action-in-target-s-has-no-inputs","errorCode":null,"errorMessage":"Action in target %s has no inputs.","messagePattern":"Action in target (.+?) has no inputs\\.","errorType":"exception","errorClass":"GypError","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/gyp/input.py","lineNumber":2818,"sourceCode":"            \"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)\n            the_dict[k] = v\n        elif isinstance(v, dict):\n            TurnIntIntoStrInDict(v)\n        elif isinstance(v, list):\n            TurnIntIntoStrInList(v)\n","sourceCodeStart":2800,"sourceCodeEnd":2836,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/gyp/input.py#L2800-L2836","documentation":"Each action must declare an 'inputs' list so the build system knows what files the action depends on. action.get('inputs', None) returning None (key absent) is rejected; an explicit empty list is accepted but pointless.","triggerScenarios":"An action dict in target_dict['actions'] that does not contain an 'inputs' key at all.","commonSituations":"Authoring an action with only 'outputs' and 'action' and omitting 'inputs'; deleting the inputs line while editing.","solutions":["Add an 'inputs' key listing the files the action reads.","If the action truly has no inputs, still provide 'inputs': [] (though this usually indicates a design issue).","Re-run gyp."],"exampleFix":"// before\n{ 'action_name': 'gen', 'action': ['gen', 'out.cc'], 'outputs': ['out.cc'] },\n// after\n{ 'action_name': 'gen', 'inputs': ['schema.txt'], 'action': ['gen', 'out.cc'], 'outputs': ['out.cc'] },","handlingStrategy":"validation","validationCode":"for a in target_dict.get('actions', []):\n    if 'inputs' not in a:\n        raise ValueError(f\"action {a.get('action_name')} has no 'inputs' key\")","typeGuard":"def actions_have_inputs(target_dict: dict) -> bool:\n    return all('inputs' in a for a in target_dict.get('actions', []))","tryCatchPattern":null,"preventionTips":["Always declare inputs even if the action only writes outputs; it drives incremental rebuilds.","Treat a missing 'inputs' key as a authoring error, not an optimization."],"tags":["gyp","actions","inputs","required-field","validation"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}