Yeachan-Heo/oh-my-codex · error · Error
Timed out acquiring task claim lock for ${teamName}/${taskId
Error message
Timed out acquiring task claim lock for ${teamName}/${taskId} What it means
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.
Source
Thrown at src/team/scaling.ts:139
taskClaimLockDir: (name: string, taskId: string, lockCwd: string) => (
join(teamDir(name, lockCwd), 'claims', `task-${taskId}.lock`)
),
mailboxLockDir: (name: string, workerName: string, lockCwd: string) => (
join(teamDir(name, lockCwd), 'mailbox', `.lock-${workerName}`)
),
};
const acquire = async (index: number): Promise<T> => {
if (index >= uniqueTaskIds.length) return await fn();
const taskId = uniqueTaskIds[index]!;
const locked = await withTaskClaimLock(
teamName,
taskId,
cwd,
TASK_CLAIM_LOCK_STALE_MS,
locks,
async () => await acquire(index + 1),
);
if (!locked.ok) throw new Error(`Timed out acquiring task claim lock for ${teamName}/${taskId}`);
return locked.value;
};
return await acquire(0);
}
// ── Environment gate ──────────────────────────────────────────────────────────
const OMX_TEAM_SCALING_ENABLED_ENV = 'OMX_TEAM_SCALING_ENABLED';
const WORKTREE_TRIGGER_STATE_ROOT = '$OMX_TEAM_STATE_ROOT';
export function isScalingEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
const raw = env[OMX_TEAM_SCALING_ENABLED_ENV];
if (!raw) return false;
const normalized = raw.trim().toLowerCase();
return ['1', 'true', 'yes', 'on', 'enabled'].includes(normalized);
}
type ScalePaneInstance = {View on GitHub (pinned to 3ad79a8a6f)
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
Defensive patterns
Strategy: retry
Validate before calling
// Check for a live lock holder before claiming const lockInfo = await readTaskClaimLock(teamName, taskId); if (lockInfo?.pid && isPidAlive(lockInfo.pid)) deferClaim();
Try / catch
try { await withTaskClaimLocks(...) } catch (e) { if (e.message.includes('Timed out acquiring task claim lock')) await sleep(backoff).then(retry); else throw e; } Prevention
- Use unique taskIds per worker to avoid contention
- Add jitter/backoff around claim retries
- Clean up stale locks after crashes
When it happens
Trigger: 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.
Common situations: Concurrent workers claiming the same task, a crashed process leaving a lock considered live, or heavy contention on the same task.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- autoresearch_active_run_exists
- timed out waiting for tmux extended-keys lease lock: ${lockP
- [native-assets] publication-lock-timeout: ${path}; elapsed=$
- question_not_open
- Timed out waiting for ultragoal mutation lock at ${repoRelat
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/e77e5753a867c131.
Report an issue: GitHub.