{"record":{"id":"c0bde09e321678f4","repo":"Yeachan-Heo/oh-my-codex","slug":"omx-warning-failed-to-stop-stale-hook-derived-w","errorCode":null,"errorMessage":"[omx] warning: failed to stop stale hook-derived watcher","messagePattern":"\\[omx\\] warning: failed to stop stale hook-derived watcher","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/cli/index.ts","lineNumber":7838,"sourceCode":"async function startHookDerivedWatcher(cwd: string): Promise<void> {\n  if (process.env.OMX_HOOK_DERIVED_SIGNALS !== \"1\") return;\n\n  const { mkdir, writeFile, readFile } = await import(\"fs/promises\");\n  const pidPath = hookDerivedWatcherPidPath(cwd);\n  const pkgRoot = getPackageRoot();\n  const watcherScript = resolveHookDerivedWatcherScript(pkgRoot);\n  if (!existsSync(watcherScript)) return;\n\n  if (existsSync(pidPath)) {\n    try {\n      const prev = JSON.parse(await readFile(pidPath, \"utf-8\")) as {\n        pid?: number;\n      };\n      if (prev && typeof prev.pid === \"number\") {\n        process.kill(prev.pid, \"SIGTERM\");\n      }\n    } catch (error: unknown) {\n      console.warn(\"[omx] warning: failed to stop stale hook-derived watcher\", {\n        path: pidPath,\n        error: error instanceof Error ? error.message : String(error),\n      });\n    }\n  }\n\n  await mkdir(join(omxRoot(cwd), \"state\"), { recursive: true }).catch(\n    (error: unknown) => {\n      console.warn(\n        \"[omx] warning: failed to create hook-derived watcher state directory\",\n        {\n          cwd,\n          error: error instanceof Error ? error.message : String(error),\n        },\n      );\n    },\n  );\n  let watcherPid: number | undefined;","sourceCodeStart":7820,"sourceCodeEnd":7856,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/index.ts#L7820-L7856","documentation":"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.","triggerScenarios":"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.","commonSituations":"User switching on a shared checkout; PID files left from a crashed session; containers with PID namespaces remapped.","solutions":["Manually kill the stale watcher: `kill $(cat .omx/state/hook-derived-watcher.pid)` or pkill the script name","Remove the stale PID file","Run subsequent omx commands as the user that owns the state directory"],"exampleFix":"// before\n# EPERM killing old watcher\n\n# after\nsudo kill $(cat .omx/state/hook-derived-watcher.pid); rm .omx/state/hook-derived-watcher.pid","handlingStrategy":"try-catch","validationCode":"function parsePid(raw: string): number | undefined {\n  const n = Number(raw);\n  return Number.isInteger(n) && n > 1 ? n : undefined;\n}","typeGuard":null,"tryCatchPattern":"try { process.kill(prev.pid,'SIGTERM'); } catch (e: any) { if (e?.code !== 'ESRCH') console.warn(...); }","preventionTips":["Sanitize pid files before killing","Use one user for all omx operations","Clean state after hard crashes"],"tags":["stale-pid-file","watcher","signal","hook"],"backgroundTag":"stale-pid-file","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}