EveryInc/compound-engineering-plugin · error · Error

Could not read ${filePath}: ${error instanceof Error ? error

Error message

Could not read ${filePath}: ${error instanceof Error ? error.message : String(error)}

What it means

readJson wraps any failure from reading or JSON.parse-ing a JSON file (package.json or .codex-plugin/plugin.json) into a single Error that prefixes the file path with the underlying message. The library throws it so callers get one consistent error shape instead of raw ENOENT/ENOTDIR/SyntaxError noise from fs.readFile. The original cause is preserved in the message text via error.message or String(error).

Source

Thrown at src/dev/codex-dev.ts:119

  const result = await runner.run(command, args, options)
  if (result.exitCode !== 0) {
    const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.exitCode}`
    throw new Error(`${command} ${args.join(" ")} failed: ${detail}`)
  }
  return result
}

function resolveHomePath(value: string, home: string): string {
  if (value === "~") return home
  if (value.startsWith("~/")) return path.join(home, value.slice(2))
  return path.resolve(value)
}

async function readJson(filePath: string): Promise<Record<string, unknown>> {
  try {
    return JSON.parse(await fs.readFile(filePath, "utf8")) as Record<string, unknown>
  } catch (error) {
    throw new Error(`Could not read ${filePath}: ${error instanceof Error ? error.message : String(error)}`)
  }
}

async function assertCompoundEngineeringRepo(repoRoot: string): Promise<void> {
  const packageJson = await readJson(path.join(repoRoot, "package.json"))
  if (packageJson.name !== "compound-engineering") {
    throw new Error(`${repoRoot} is not the compound-engineering repository`)
  }
  const pluginJson = await readJson(path.join(repoRoot, ".codex-plugin", "plugin.json"))
  if (pluginJson.name !== "compound-engineering") {
    throw new Error(`${repoRoot} is not the compound-engineering repository`)
  }

  const skills = pluginJson.skills
  if (typeof skills !== "string" || path.resolve(repoRoot, skills) !== path.join(repoRoot, "skills")) {
    throw new Error("The Codex plugin manifest does not point at this repository's skills directory")
  }

View on GitHub (pinned to c9c10f8c75)

Solutions

  1. Verify the file exists at the printed path: ls <path from the error message>.
  2. If missing, run the command from inside a compound-engineering checkout (git rev-parse --show-toplevel should show the repo root).
  3. If present but unparseable, validate it: cat <path> | jq . — fix or git restore the JSON.
  4. Check read permissions on the file if the message mentions EACCES.

Example fix

// before: running tooling from an arbitrary directory
bun run codex:dev -- status   // Could not read /home/user/package.json: ENOENT
// after: cd into the checkout first
cd ~/src/compound-engineering && bun run codex:dev -- status
Defensive patterns

Strategy: try-catch

Validate before calling

import { stat, readFile } from "node:fs/promises"
async function assertReadableJson(p: string) {
  const s = await stat(p)
  if (!s.isFile()) throw new Error(`${p} is not a file`)
  JSON.parse(await readFile(p, "utf8")) // throws SyntaxError before the call
}

Type guard

function isRecord(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null && !Array.isArray(v)
}

Try / catch

try {
  const pkg = await packageJson(repoRoot)
} catch (error) {
  if ((error as NodeJS.ErrnoException).cause) console.error("underlying fs/parse error:", (error as Error).message)
  console.error(`Run from the compound-engineering checkout; failed to read JSON: ${(error as Error).message}`)
  process.exitCode = 1
}

Prevention

When it happens

Trigger: Calling packageJson() or pluginJson() (or readJson directly) when the file does not exist, the path is a directory, permissions deny read, or the file content is not valid JSON — any fs.readFile rejection or JSON.parse throw inside readJson at src/dev/codex-dev.ts:115.

Common situations: Running the codex:dev workflow outside a repo checkout so package.json is missing; a truncated or hand-edited plugin.json that fails to parse; a checkout with core.symlinks issues where .codex-plugin/plugin.json resolves to nothing; running from a worktree stripped of dot-directories.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31). Data as JSON: /api/errors/c7c947fa78d7cfe1. Report an issue: GitHub.