JuliusBrussee/caveman · error · Error

CODEX_HOME points to

Error message

CODEX_HOME points to ${JSON.stringify(configured)}, but that path is not a directory

What it means

Thrown when CODEX_HOME is set and stat succeeds, but the path is not a directory (e.g. it is a regular file, symlink to a file, or socket). The CLI requires CODEX_HOME to be a directory because it stores auth.json and other state under it.

Solutions

  1. Check the path: `file "$CODEX_HOME"` or `ls -ld` — replace it with a directory
  2. Point CODEX_HOME at a proper directory (create it with mkdir -p if needed) or unset CODEX_HOME to use the default (~/.codex)
  3. If a file occupies the intended path, move the file aside and create the directory

Example fix

// before
export CODEX_HOME=/home/user/.codex/auth.json
// after
export CODEX_HOME=/home/user/.codex
Defensive patterns

Strategy: validation

Validate before calling

const configured = process.env.CODEX_HOME;
if (configured && fs.existsSync(configured) && !fs.statSync(configured).isDirectory()) {
  throw new Error(`CODEX_HOME must be a directory, got: ${configured}`);
}

Type guard

function isDirectoryPath(p: string): boolean {
  try { return fs.statSync(p).isDirectory(); } catch { return false; }
}

Try / catch

try {
  runCodexCommand();
} catch (e) {
  if (String(e.message).includes("is not a directory")) {
    console.error(`CODEX_HOME must point to a directory; got ${process.env.CODEX_HOME}`);
  }
}

Prevention

When it happens

Trigger: CODEX_HOME env var points to an existing non-directory path, so metadata.isDirectory() returns false during codexHomeDir().

Common situations: CODEX_HOME accidentally set to the auth.json file itself, to a downloaded archive, or to a path that was replaced by a file during a migration/restore.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/3846e385dbbd6be6. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/index.ts:6203

}

export function codexHomeDir(env: NodeJS.ProcessEnv = process.env): string {
  const configured = env.CODEX_HOME;
  if (!configured) return join(homedir(), ".codex");
  // 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();

View on GitHub (pinned to 3ee70a1026)