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
- Verify the file exists at the printed path: ls <path from the error message>.
- If missing, run the command from inside a compound-engineering checkout (git rev-parse --show-toplevel should show the repo root).
- If present but unparseable, validate it: cat <path> | jq . — fix or git restore the JSON.
- 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
- Always run codex:dev commands from inside the plugin checkout.
- Validate plugin.json/package.json with jq after hand edits.
- Check the wrapped message for ENOENT vs SyntaxError to distinguish missing file from bad JSON.
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
- Could not inspect ${configPath}: ${error instanceof Error ?
- Local plugin path not found: ${directPath}
- Local plugin path not found: ${directPath}
- Plugin directory not found: ${pluginPath}
- ${repoRoot} is not the compound-engineering repository
AI-assisted analysis of EveryInc/compound-engineering-plugin@c9c10f8c75 (2026-08-31).
Data as JSON: /api/errors/c7c947fa78d7cfe1.
Report an issue: GitHub.