Yeachan-Heo/oh-my-codex · error · Error

auth path is not a directory: ${dir}

Error message

auth path is not a directory: ${dir}

What it means

In the final batch proof pass of requireFrozenWindowTopologySync, a pane is live but either its returned pane id or PID doesn't match the frozen expectation. This is the definitive signal that the pane identity was recycled or the proof read raced a pane swap, so the frozen contract is void.

Source

Thrown at src/auth/paths.ts:58

export function resolveDefaultCodexHome(home = homedir()): string {
  return join(home, ".codex");
}

export function resolveLiveAuthPath(
  cwd = process.cwd(),
  env: NodeJS.ProcessEnv = process.env,
  home = homedir(),
): string {
  const codexHome = resolveCodexHomeForLaunch(cwd, env) || resolveDefaultCodexHome(home);
  return join(codexHome, "auth.json");
}

export async function ensurePrivateDir(dir: string): Promise<void> {
  await mkdir(dir, { recursive: true, mode: AUTH_DIR_MODE });
  const info = await lstat(dir);
  if (info.isSymbolicLink()) throw new Error(`auth directory must not be a symlink: ${dir}`);
  if (!info.isDirectory()) throw new Error(`auth path is not a directory: ${dir}`);
  if (process.platform !== "win32") {
    const { chmod } = await import("fs/promises");
    await chmod(dir, AUTH_DIR_MODE).catch(() => undefined);
  }
}

export async function assertReadableFile(path: string, label: string): Promise<void> {
  try {
    const info = await stat(path);
    if (!info.isFile()) throw new Error(`${label} is not a file: ${path}`);
  } catch (err) {
    if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") {
      throw new Error(`${label} not found: ${path}`);
    }
    throw err;
  }
}

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Never persist expectedPanePids across tmux server restarts — re-capture after any restart
  2. Disable respawn/auto-recreation for team-managed panes
  3. Recreate the team session to re-freeze a consistent topology
  4. Check for a second concurrent team session touching the same window
Defensive patterns

Strategy: validation

Validate before calling

const proofs = readExactPaneProofsSync([...expectedPanePids.keys()]); proofs.every((p, i) => p.status === 'live' && p.pid === [...expectedPanePids.values()][i]);

Try / catch

catch (err) { if (/pane identity changed/.test(err.message)) { /* topology invalid: recreate session, do not retry */ } }

Prevention

When it happens

Trigger: Pane id/PID churn (respawn, pane recreation, window swap) occurring between topology freeze, first validation, and final batch proof; or expectedPanePids built from a different session incarnation.

Common situations: Respawn automation, multiple orchestrators mutating the same window, or stale topology snapshots persisted across tmux server restarts (tmux server restart resets pane ids).

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/31b636c7c95318e6. Report an issue: GitHub.