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_dict

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Add a 'targets' array to the .gyp file (even if some values come from target_defaults).
  2. 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.
  3. 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

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


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/80fd333c248a7e8c. Report an issue: GitHub.