Yeachan-Heo/oh-my-codex · warning

[omx] warning: failed to stop stale hook-derived watcher

Error message

[omx] warning: failed to stop stale hook-derived watcher

What it means

Same as the notify-fallback variant but for the hook-derived watcher: the CLI found a previous watcher PID file and process.kill(prev.pid, 'SIGTERM') threw (e.g. EPERM or invalid PID). The old watcher may keep running alongside a new one.

Source

Thrown at src/cli/index.ts:7838

async function startHookDerivedWatcher(cwd: string): Promise<void> {
  if (process.env.OMX_HOOK_DERIVED_SIGNALS !== "1") return;

  const { mkdir, writeFile, readFile } = await import("fs/promises");
  const pidPath = hookDerivedWatcherPidPath(cwd);
  const pkgRoot = getPackageRoot();
  const watcherScript = resolveHookDerivedWatcherScript(pkgRoot);
  if (!existsSync(watcherScript)) return;

  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;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Manually kill the stale watcher: `kill $(cat .omx/state/hook-derived-watcher.pid)` or pkill the script name
  2. Remove the stale PID file
  3. Run subsequent omx commands as the user that owns the state directory

Example fix

// before
# EPERM killing old watcher

# after
sudo kill $(cat .omx/state/hook-derived-watcher.pid); rm .omx/state/hook-derived-watcher.pid
Defensive patterns

Strategy: try-catch

Validate before calling

function parsePid(raw: string): number | undefined {
  const n = Number(raw);
  return Number.isInteger(n) && n > 1 ? n : undefined;
}

Try / catch

try { process.kill(prev.pid,'SIGTERM'); } catch (e: any) { if (e?.code !== 'ESRCH') console.warn(...); }

Prevention

When it happens

Trigger: Stopping a stale hook-derived watcher whose recorded PID belongs to another user (EPERM) or no longer maps to a valid signal target in a way that throws a non-ESRCH error.

Common situations: User switching on a shared checkout; PID files left from a crashed session; containers with PID namespaces remapped.

Related errors


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