nodejs/node · error · RuntimeError

Unable to find a valid ninja output directory.

Error message

Unable to find a valid ninja output directory.

What it means

Raised by GetNinjaOutputDirectory in deps/v8/tools/vim/ninja_output.py when generate_paths() yields no candidate build directories, so max(..., key=...) raises ValueError, which is caught and re-raised as this RuntimeError. It is used by V8's Vim syntastic/ale integration to locate the build output for clangd/compilation.

Source

Thrown at deps/v8/tools/vim/ninja_output.py:65

        path = os.path.join(out_path, config)
        if os.path.exists(os.path.join(path, 'build.ninja')):
          yield path

  def approx_directory_mtime(path):
    # This is a heuristic; don't recurse into subdirectories.
    paths = [path] + [os.path.join(path, f) for f in os.listdir(path)]
    return max(filter(None, [safe_mtime(p) for p in paths]))

  def safe_mtime(path):
    try:
      return os.path.getmtime(path)
    except OSError:
      return None

  try:
    return max(generate_paths(), key=approx_directory_mtime)
  except ValueError:
    raise RuntimeError('Unable to find a valid ninja output directory.')


if __name__ == '__main__':
  if len(sys.argv) != 2:
    raise RuntimeError('Expected a single path argument.')
  print(GetNinjaOutputDirectory(sys.argv[1]))

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Build first: run gn gen out/Release (or out/build) then ninja -C out/Release so an output directory exists.
  2. Confirm the path passed to GetNinjaOutputDirectory is the V8 source root containing the out/ tree.
  3. If using a custom output dir, create a symlink or adjust the heuristics in ninja_output.py to include it.

Example fix

# before (no build output yet)
:make
# after
gn gen out/Release && ninja -C out/Release
# then re-run the Vim command
Defensive patterns

Strategy: validation

Validate before calling

import os
def has_build_output(project_root: str) -> bool:
    return any(os.path.isdir(os.path.join(project_root, d)) for d in ('out','build')) and \
           any(os.path.isdir(os.path.join(project_root, 'out', c)) for c in os.listdir(os.path.join(project_root, 'out'))) if os.path.isdir(os.path.join(project_root,'out')) else False

Type guard

null

Try / catch

from tools.vim.ninja_output import GetNinjaOutputDirectory
try:
    out = GetNinjaOutputDirectory(project_root)
except RuntimeError:
    out = None
    print('Build the project first: gn gen out/Release && ninja -C out/Release')

Prevention

When it happens

Trigger: Calling GetNinjaOutputDirectory(path) where no out/<config> or build output directory exists under the given project path; all generated candidate paths are empty so approx_directory_mtime has nothing to compare.

Common situations: Using the V8 Vim integration before running gn gen / ninja; pointing the script at a fresh checkout with no build artifacts; the build output lives in a non-standard location the heuristics don't search.

Related errors


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