Yeachan-Heo/oh-my-codex · warning

[omx] warning: failed to create hook-derived watcher state d

Error message

[omx] warning: failed to create hook-derived watcher state directory

What it means

The CLI could not create <omxRoot>/state for the hook-derived watcher (mkdir rejected and swallowed). The watcher PID cannot be persisted, so lifecycle tracking is degraded.

Source

Thrown at src/cli/index.ts:7847

  if (existsSync(pidPath)) {
    try {
      const prev = JSON.parse(await readFile(pidPath, "utf-8")) as {
        pid?: number;
      };
      if (prev && typeof prev.pid === "number") {
        process.kill(prev.pid, "SIGTERM");
      }
    } catch (error: unknown) {
      console.warn("[omx] warning: failed to stop stale hook-derived watcher", {
        path: pidPath,
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  await mkdir(join(omxRoot(cwd), "state"), { recursive: true }).catch(
    (error: unknown) => {
      console.warn(
        "[omx] warning: failed to create hook-derived watcher state directory",
        {
          cwd,
          error: error instanceof Error ? error.message : String(error),
        },
      );
    },
  );
  let watcherPid: number | undefined;
  try {
    watcherPid = await launchBackgroundHelper([watcherScript, "--cwd", cwd], {
      cwd,
      env: process.env,
    });
  } catch (error: unknown) {
    console.warn("[omx] warning: failed to launch hook-derived watcher", {
      cwd,
      error: error instanceof Error ? error.message : String(error),

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Repair .omx permissions or ownership
  2. Remove the blocking file and create the directory
  3. Relocate state to a writable path via omx config

Example fix

// before
rm -rf .omx   # owned by root

# after
sudo chown -R $USER .omx && mkdir -p .omx/state
Defensive patterns

Strategy: validation

Validate before calling

import { existsSync, statSync, accessSync, constants } from 'node:fs';
function canCreateState(root: string): boolean {
  const dir = `${root}/state`;
  if (existsSync(dir)) return statSync(dir).isDirectory();
  try { accessSync(root, constants.W_OK); return true; } catch { return false; }
}

Prevention

When it happens

Trigger: mkdir(join(omxRoot(cwd),'state'),{recursive:true}) rejects: EACCES/EROFS, or a non-directory file exists at the path.

Common situations: Read-only project mounts; .omx owned by another user; a stray file named 'state'; restrictive container security profiles.

Related errors


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