JuliusBrussee/caveman · error · Error
failed to canonicalize CODEX_HOME
Error message
failed to canonicalize CODEX_HOME ${JSON.stringify(configured)}: ${(error as Error).message} What it means
Thrown when realpathSync fails while canonicalizing an existing CODEX_HOME directory (it passed the stat and isDirectory checks). This resolves symlinks to an absolute canonical path; failure here usually means the path became inaccessible between stat and realpath, or a symlink loop / permission issue on a symlinked component.
Solutions
- Re-check the path exists and is traversable: `ls -la` on CODEX_HOME and ancestors
- Fix symlink loops: inspect with `namei -l "$CODEX_HOME"` and remove the cyclic link
- Restore read/execute permissions on symlinked directories in the path
- Retry; if the directory was concurrently deleted, recreate it with mkdir -p
Example fix
// before (symlink loop) ln -s .codex .codex/loop // after rm .codex/loop && mkdir -p "$CODEX_HOME"
Defensive patterns
Strategy: try-catch
Validate before calling
try { fs.realpathSync(process.env.CODEX_HOME ?? ""); } catch (e) { console.error(`CODEX_HOME cannot be canonicalized: ${e.message}`); } Type guard
function isRealCanonicalizableDir(p: string): boolean {
try { return fs.statSync(p).isDirectory() && !!fs.realpathSync(p); } catch { return false; }
} Try / catch
try {
runCodexCommand();
} catch (e) {
if (String(e.message).startsWith("failed to canonicalize CODEX_HOME")) {
console.error("Check CODEX_HOME for symlink loops or permission changes: " + e.message);
}
} Prevention
- Avoid symlink loops in CODEX_HOME paths
- Keep read/execute permissions stable on symlinked config directories
- Use plain absolute directories instead of nested symlink chains for CODEX_HOME
When it happens
Trigger: realpathSync(configured) throws — typically EACCES on a symlink component, ELOOP from a symlink cycle, or ENOENT if the path was deleted between stat and realpath.
Common situations: CODEX_HOME containing a self-referencing symlink loop, permissions tightened on ~/.codex or an ancestor after stat, or the directory being concurrently removed.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- failed to read CODEX_HOME
- caveman build: package artifact symlink is not lockable
- caveman build: source graph symlink escapes project root
- CODEX_HOME points to
- CODEX_HOME points to
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5cd791b6408231d5.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/index.ts:6208
// Match Codex's own home resolver: a nonempty override may be relative, but
// must already be a directory and is canonicalized before use. Never fall
// back to another account's default home when the override is invalid.
let metadata: ReturnType<typeof statSync>;
try {
metadata = statSync(configured);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
throw new Error(`CODEX_HOME points to ${JSON.stringify(configured)}, but that path does not exist`);
}
throw new Error(`failed to read CODEX_HOME ${JSON.stringify(configured)}: ${(error as Error).message}`);
}
if (!metadata.isDirectory()) {
throw new Error(`CODEX_HOME points to ${JSON.stringify(configured)}, but that path is not a directory`);
}
try {
return realpathSync(configured);
} catch (error) {
throw new Error(`failed to canonicalize CODEX_HOME ${JSON.stringify(configured)}: ${(error as Error).message}`);
}
}
function codexAuthPath(): string {
return join(codexHomeDir(), "auth.json");
}
function nonEmptyString(v: unknown): v is string {
return typeof v === "string" && v.trim().length > 0;
}
function readCodexAuthJson(): JsonObject | undefined {
const path = codexAuthPath();
try {
const stat = statSync(path);
if (!stat.isFile() || stat.size === 0) return undefined;
const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
return asJsonObject(parsed);View on GitHub (pinned to 3ee70a1026)