nodejs/node · error · TypeError

Unknown AST node at key path '%s': %s

Error message

Unknown AST node at key path '%s': %s

What it means

Raised as a TypeError by CheckNode during checked .gyp evaluation when the AST contains a node type other than Dict, List, or Str. Gyp's strict check mode only permits string literals, lists, and dicts at the value level; anything else (numbers, booleans, None, name references, arithmetic, function calls) is rejected because gyp files are meant to be declarative data.

Source

Thrown at tools/gyp/pylib/gyp/input.py:218

                    + " with key path '"
                    + ".".join(keypath)
                    + "'"
                )
            kp = list(keypath)  # Make a copy of the list for descending this node.
            kp.append(key)
            dict[key] = CheckNode(value, kp)
        return dict
    elif isinstance(node, ast.List):
        children = []
        for index, child in enumerate(node.elts):
            kp = list(keypath)  # Copy list.
            kp.append(repr(index))
            children.append(CheckNode(child, kp))
        return children
    elif isinstance(node, ast.Str):
        return node.s
    else:
        raise TypeError(
            "Unknown AST node at key path '" + ".".join(keypath) + "': " + repr(node)
        )


def LoadOneBuildFile(build_file_path, data, aux_data, includes, is_target, check):
    if build_file_path in data:
        return data[build_file_path]

    if os.path.exists(build_file_path):
        build_file_contents = open(build_file_path, encoding="utf-8").read()
    else:
        raise GypError(f"{build_file_path} not found (cwd: {os.getcwd()})")

    build_file_data = None
    try:
        if check:
            build_file_data = CheckedEval(build_file_contents)
        else:

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Convert non-string scalar values to quoted string literals (e.g. 3 -> '3', true -> 'true').
  2. Remove variable references and Python expressions; gyp files must be pure literal data in check mode.
  3. If you need computed values, use gyp variables and conditionals rather than inline expressions.

Example fix

// before
'targets': [{ 'target_name': 'foo', 'version': 3, 'enabled': true }]
// after
'targets': [{ 'target_name': 'foo', 'version': '3', 'enabled': 'true' }]
Defensive patterns

Strategy: validation

Validate before calling

import ast
_ALLOWED = (ast.Dict, ast.List, ast.Str)
for fn in gyp_files:
    tree = ast.parse(open(fn).read())
    for node in ast.walk(tree):
        if not isinstance(node, (ast.Module, ast.Expr)+_ALLOWED) and not isinstance(ast.dump(node), str):
            pass
def check(node):
    if isinstance(node, ast.Dict) or isinstance(node, ast.List):
        return True
    return isinstance(node, ast.Str)

Type guard

import ast
def is_allowed_node(node) -> bool:
    return isinstance(node, (ast.Dict, ast.List, ast.Str))

Prevention

When it happens

Trigger: A .gyp file evaluated with check=True contains a numeric literal, a boolean, a bare identifier, a unary/binary expression, or a function call at a value position. CheckNode's isinstance chain falls through to the `else` at input.py:219.

Common situations: Using a number directly (e.g. 'version': 3) instead of a string ('version': '3'); referencing a variable by name; pasting Python expressions into a gyp file; using True/False/None literals.

Related errors


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