{"record":{"id":"26e2e80c72c6c81d","repo":"Yeachan-Heo/oh-my-codex","slug":"failed-to-acquire-agents-md-lock-within-timeout","errorCode":null,"errorMessage":"Failed to acquire AGENTS.md lock within timeout","messagePattern":"Failed to acquire AGENTS\\.md lock within timeout","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/hooks/agents-overlay.ts","lineNumber":104,"sourceCode":"        const ownerFile = join(lock, \"owner.json\");\n        const ownerData = JSON.parse(await readFile(ownerFile, \"utf-8\"));\n        try {\n          process.kill(ownerData.pid, 0);\n        } catch {\n          // Owner PID is dead, safe to reap\n          await rm(lock, { recursive: true, force: true }).catch(() => {});\n          continue; // Retry acquire immediately\n        }\n      } catch (err) {\n        process.stderr.write(\n          `[agents-overlay] lock owner check failed: ${err}\\n`,\n        );\n      }\n      await new Promise((r) => setTimeout(r, 100));\n    }\n  }\n  // Timeout: do NOT silently proceed - throw so caller knows lock failed\n  throw new Error(\"Failed to acquire AGENTS.md lock within timeout\");\n}\n\nasync function releaseLock(cwd: string): Promise<void> {\n  try {\n    await rm(lockPath(cwd), { recursive: true, force: true });\n  } catch (err) {\n    process.stderr.write(`[agents-overlay] release lock failed: ${err}\\n`);\n  }\n}\n\nasync function withAgentsMdLock<T>(\n  cwd: string,\n  fn: () => Promise<T>,\n): Promise<T> {\n  await acquireLock(cwd);\n  try {\n    return await fn();\n  } finally {","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/hooks/agents-overlay.ts#L86-L122","documentation":"Thrown by acquireLock in the AGENTS.md overlay hook when it cannot acquire the advisory lock directory within its timeout budget (it retries every 100ms then gives up). The library explicitly fails rather than silently proceeding, so concurrent writers to AGENTS.md don't clobber each other.","triggerScenarios":"Calling withAgentsMdLock while another process/hook holds the .agents-md lock directory for longer than the timeout; a stale lock directory left behind by a crashed process; heavy parallel hook invocations (e.g. many concurrent agent sessions) exceeding the retry window.","commonSituations":"Multiple opencode/agent instances started simultaneously in the same repo; a previous run killed with SIGKILL leaving a stale lock dir; slow filesystems (NFS, containers) where lock dir creation and the 100ms retry loop exceed the timeout.","solutions":["Remove the stale lock directory (the path from lockPath(cwd), typically under the repo's hook state dir) if no other process is running, then retry","Reduce concurrency: serialize hook invocations that write AGENTS.md so only one holds the lock at a time","Check for a hung process holding the lock (ps / lsof) and terminate it","If on a slow/shared filesystem, increase the lock timeout if configurable, or move the repo to local disk"],"exampleFix":"// before\nawait withAgentsMdLock(cwd, writeOverlay);\n\n// after\n// clear stale lock when no other instance is running\nawait rm(lockDirPath, { recursive: true, force: true });\nawait withAgentsMdLock(cwd, writeOverlay);","handlingStrategy":"retry","validationCode":"import { stat } from 'node:fs/promises';\n\nasync function lockLikelyFree(cwd: string): Promise<boolean> {\n  try { await stat(lockPathFor(cwd)); return false; } catch { return true; }\n}","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  try { return await withAgentsMdLock(cwd, fn); }\n  catch (err) {\n    if (!(err instanceof Error) || !err.message.includes('AGENTS.md lock')) throw err;\n    await new Promise(r => setTimeout(r, 250 * (attempt + 1)));\n  }\n}\nthrow new Error('AGENTS.md overlay lock unavailable after retries');","preventionTips":["Serialize AGENTS.md-writing hooks across processes (queue, single writer)","Clean up stale lock directories after crashes in a startup health check","Avoid running many agent sessions concurrently in the same checkout"],"tags":["lock","concurrency","filesystem","hooks"],"backgroundTag":"lock-acquisition-timeout","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}