jackwener/OpenCLI · critical

Could not find package.json above ${startFile}

Error message

Could not find package.json above ${startFile}

What it means

findPackageRoot() walks upward from startFile's directory looking for a package.json to identify the package root. If it reaches the filesystem root (parent === dir) without finding one, it throws this Error. The library relies on the package root to locate built assets such as the builtin CLIs directory.

Source

Thrown at src/package-paths.ts:16

import * as fs from 'node:fs';
import * as path from 'node:path';

export interface PackageJsonLike {
  bin?: string | Record<string, string>;
  main?: string;
}

export function findPackageRoot(startFile: string, fileExists: (candidate: string) => boolean = fs.existsSync): string {
  let dir = path.dirname(startFile);

  while (true) {
    if (fileExists(path.join(dir, 'package.json'))) return dir;
    const parent = path.dirname(dir);
    if (parent === dir) {
      throw new Error(`Could not find package.json above ${startFile}`);
    }
    dir = parent;
  }
}

export function getBuiltEntryCandidates(
  packageRoot: string,
  readFile: (filePath: string) => string = (filePath) => fs.readFileSync(filePath, 'utf-8'),
): string[] {
  const candidates: string[] = [];
  try {
    const pkg = JSON.parse(readFile(path.join(packageRoot, 'package.json'))) as PackageJsonLike;

    if (typeof pkg.bin === 'string') {
      candidates.push(path.join(packageRoot, pkg.bin));
    } else if (pkg.bin && typeof pkg.bin === 'object' && typeof pkg.bin.opencli === 'string') {
      candidates.push(path.join(packageRoot, pkg.bin.opencli));
    }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Ensure a package.json exists at the project root and in every ancestor directory between startFile and the root.
  2. If bundling/packaging, include package.json in the output alongside the entry file.
  3. Run the code from within the installed package directory rather than a copied-out script.
  4. In containers, keep package.json in the image next to the built sources.

Example fix

// before (docker prune step)
RUN rm -rf /app/package.json
// after
RUN cp /app/package.json /dist/  # or simply keep it in place
Defensive patterns

Strategy: try-catch

Validate before calling

import fs from 'node:fs';
import path from 'node:path';
function hasPackageJsonAbove(startFile) {
  let dir = path.dirname(path.resolve(startFile));
  while (true) {
    if (fs.existsSync(path.join(dir, 'package.json'))) return true;
    const parent = path.dirname(dir);
    if (parent === dir) return false;
    dir = parent;
  }
}
// if (!hasPackageJsonAbove(__filename)) fail early with a clear deployment error

Try / catch

let projectRoot;
try {
  projectRoot = findPackageRoot(__filename);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Could not find package.json above')) {
    projectRoot = process.env.APP_ROOT ?? process.cwd();
    logger.warn(`No package.json found; assuming project root ${projectRoot}`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling findPackageRoot (directly or via PACKAGE_ROOT/packageRoot/projectRoot/BUILTIN_CLIS/defaultBuiltinClisDir) with a startFile path outside any package — e.g. inside /tmp, a bare system directory, a deployed bundle that strips package.json, or a deleted/moved project without package.json at any ancestor level.

Common situations: Running from a global install or standalone binary, container images built with `--no-package-json` pruning, monorepo code copied to a scratch dir, tests running with a tmp cwd outside the repo, or packaged apps (pkg/nexe) that virtualize the filesystem.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/0fcf0f2f094481ec. Report an issue: GitHub.