{"record":{"id":"e77e5753a867c131","repo":"Yeachan-Heo/oh-my-codex","slug":"timed-out-acquiring-task-claim-lock-for-teamname","errorCode":null,"errorMessage":"Timed out acquiring task claim lock for ${teamName}/${taskId}","messagePattern":"Timed out acquiring task claim lock for (.+?)/(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/team/scaling.ts","lineNumber":139,"sourceCode":"    taskClaimLockDir: (name: string, taskId: string, lockCwd: string) => (\n      join(teamDir(name, lockCwd), 'claims', `task-${taskId}.lock`)\n    ),\n    mailboxLockDir: (name: string, workerName: string, lockCwd: string) => (\n      join(teamDir(name, lockCwd), 'mailbox', `.lock-${workerName}`)\n    ),\n  };\n  const acquire = async (index: number): Promise<T> => {\n    if (index >= uniqueTaskIds.length) return await fn();\n    const taskId = uniqueTaskIds[index]!;\n    const locked = await withTaskClaimLock(\n      teamName,\n      taskId,\n      cwd,\n      TASK_CLAIM_LOCK_STALE_MS,\n      locks,\n      async () => await acquire(index + 1),\n    );\n    if (!locked.ok) throw new Error(`Timed out acquiring task claim lock for ${teamName}/${taskId}`);\n    return locked.value;\n  };\n  return await acquire(0);\n}\n\n// ── Environment gate ──────────────────────────────────────────────────────────\n\nconst OMX_TEAM_SCALING_ENABLED_ENV = 'OMX_TEAM_SCALING_ENABLED';\nconst WORKTREE_TRIGGER_STATE_ROOT = '$OMX_TEAM_STATE_ROOT';\n\nexport function isScalingEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n  const raw = env[OMX_TEAM_SCALING_ENABLED_ENV];\n  if (!raw) return false;\n  const normalized = raw.trim().toLowerCase();\n  return ['1', 'true', 'yes', 'on', 'enabled'].includes(normalized);\n}\n\ntype ScalePaneInstance = {","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/team/scaling.ts#L121-L157","documentation":"Thrown by the task claim lock acquisition in scaling.ts: after stale-lock takeover attempts, the named team/task claim lock could not be acquired within the timeout window.","triggerScenarios":"Acquiring task claim locks (directly or via withTaskClaimLocks) when another process holds the lock for teamName/taskId and does not release it within TASK_CLAIM_LOCK_STALE_MS plus retry budget.","commonSituations":"Concurrent workers claiming the same task, a crashed process leaving a lock considered live, or heavy contention on the same task.","solutions":["Check for a live process holding the claim lock (lock file records pid) and let it finish or kill it","Retry after the stale window elapses so the lock can be taken over","Avoid having multiple workers claim the identical taskId simultaneously"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Check for a live lock holder before claiming\nconst lockInfo = await readTaskClaimLock(teamName, taskId);\nif (lockInfo?.pid && isPidAlive(lockInfo.pid)) deferClaim();","typeGuard":null,"tryCatchPattern":"try { await withTaskClaimLocks(...) } catch (e) { if (e.message.includes('Timed out acquiring task claim lock')) await sleep(backoff).then(retry); else throw e; }","preventionTips":["Use unique taskIds per worker to avoid contention","Add jitter/backoff around claim retries","Clean up stale locks after crashes"],"tags":["locking","concurrency","scaling","timeout"],"backgroundTag":"lock-acquisition-timeout","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}