nodejs/node · error · GypError
Unable to find targets in build file %s
Error message
Unable to find targets in build file %s
What it means
Raised as a GypError by LoadTargetBuildFile when a .gyp file declares a 'target_defaults' block but no 'targets' block. target_defaults is meant to supply shared defaults that individual targets inherit; defining defaults without any targets to apply them to is treated as a structural error, since the file would produce no output.
Source
Thrown at tools/gyp/pylib/gyp/input.py:439
# Do a first round of toolsets expansion so that conditions can be defined
# per toolset.
ProcessToolsetsInDict(build_file_data)
# Apply "pre"/"early" variable expansions and condition evaluations.
ProcessVariablesAndConditionsInDict(
build_file_data, PHASE_EARLY, variables, build_file_path
)
# Since some toolsets might have been defined conditionally, perform
# a second round of toolsets expansion now.
ProcessToolsetsInDict(build_file_data)
# Look at each project's target_defaults dict, and merge settings into
# targets.
if "target_defaults" in build_file_data:
if "targets" not in build_file_data:
raise GypError("Unable to find targets in build file %s" % build_file_path)
index = 0
while index < len(build_file_data["targets"]):
# This procedure needs to give the impression that target_defaults is
# used as defaults, and the individual targets inherit from that.
# The individual targets need to be merged into the defaults. Make
# a deep copy of the defaults for each target, merge the target dict
# as found in the input file into that copy, and then hook up the
# copy with the target-specific data merged into it as the replacement
# target dict.
old_target_dict = build_file_data["targets"][index]
new_target_dict = gyp.simple_copy.deepcopy(
build_file_data["target_defaults"]
)
MergeDicts(
new_target_dict, old_target_dict, build_file_path, build_file_path
)
build_file_data["targets"][index] = new_target_dictView on GitHub (pinned to 1b2de5e052)
Solutions
- Add a 'targets' array to the .gyp file (even if some values come from target_defaults).
- If the file is meant only to hold shared defaults, convert it to a .gypi include consumed by other .gyp files rather than a standalone target file.
- Move target_defaults into a common.gypi that is included where targets actually live.
Example fix
// before (foo.gyp)
{
'target_defaults': { 'include_dirs': ['include'] }
}
// after
{
'target_defaults': { 'include_dirs': ['include'] },
'targets': [
{ 'target_name': 'foo', 'type': 'static_library', 'sources': ['foo.cc'] }
]
} Defensive patterns
Strategy: validation
Validate before calling
if 'target_defaults' in build_file_data and 'targets' not in build_file_data:
raise ValueError('file has target_defaults but no targets; add a targets array') Type guard
def defaults_have_targets(data) -> bool:
return 'target_defaults' not in data or 'targets' in data Prevention
- Always include a 'targets' array when defining 'target_defaults'.
- Move pure defaults into a .gypi include consumed by target-bearing files.
When it happens
Trigger: A target .gyp file contains 'target_defaults' but no 'targets' key. The check at input.py:442 fires because target_defaults is only processed when targets exist to receive the defaults.
Common situations: Splitting a .gyp and forgetting to add the 'targets' array; a refactor that moved targets into another file but left target_defaults behind; misunderstanding that target_defaults supplements rather than replaces targets.
Related errors
- %s does not evaluate to a dictionary.
- Key '%s' repeated at level %s with key path '%s'
- %s must not contain included_files key
- Unable to open file %s
- config_path must be a JSON file containing a dictionary
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/80fd333c248a7e8c.
Report an issue: GitHub.