Yeachan-Heo/oh-my-codex · error · Error
worker_notify_failed
Error message
worker_notify_failed
What it means
Thrown after the post-claim worker notification loop when the final dispatch outcome is not ok. The task was successfully claimed but the worker (e.g. its tmux pane/agent session) could not be notified to start work; the surrounding catch then releases the claim.
Source
Thrown at src/team/runtime.ts:4787
if (outcome.ok) break;
if (attempt < maxAssignRetries && config.worker_launch_mode === 'interactive' && config.tmux_session) {
if (dismissTrustPromptIfPresent(config.tmux_session, workerInfo.index, workerInfo.pane_id, workerInfo.pid, config.tmux_pane_owner_id ?? undefined, config.hud_pane_id ?? undefined)) {
waitForWorkerReady(
config.tmux_session,
workerInfo.index,
resolveWorkerReadyTimeoutMs(process.env),
workerInfo.pane_id,
workerInfo.pid,
config.tmux_pane_owner_id ?? undefined,
config.hud_pane_id ?? undefined,
);
} else {
await new Promise<void>(r => setTimeout(r, assignRetryDelayS * 1000));
}
}
}
if (!outcome.ok) {
throw new Error('worker_notify_failed');
}
} catch (error) {
// Roll back claim to avoid stuck in_progress tasks on any post-claim dispatch failure.
const released = await releaseTaskClaim(sanitized, taskId, claim.claimToken, workerName, cwd);
const reason = error instanceof Error && error.message.trim() !== ''
? error.message
: 'worker_assignment_failed';
try {
await writeWorkerInbox(
sanitized,
workerName,
`# Assignment Cancelled\n\nTask ${taskId} was not dispatched due to ${reason}.\nDo not execute this task from prior inbox content.`,
cwd,
);
} catch (err) {
process.stderr.write(`[team/runtime] operation failed: ${err}\n`);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Verify the worker pane/process is alive (isWorkerAlive / tmux list-panes) before assigning
- If the pane is dead, restart or rescale the worker, then re-assign (the claim is auto-released)
- Accept pending trust prompts for the worker session so dispatch can succeed
Example fix
// before
await assignTask(team, taskId, worker, cwd);
// after
if (!isWorkerAlive(config.tmux_session, w.index, w.pane_id, w.pid)) {
await rescaleWorker(team, worker); // recreate dead pane
}
await assignTask(team, taskId, worker, cwd); Defensive patterns
Strategy: retry
Validate before calling
const alive = isWorkerAlive(config.tmux_session, w.index, w.pane_id, w.pid, config.tmux_pane_owner_id, config.hud_pane_id);
if (!alive) throw new Error('worker pane dead; restart before dispatch'); Type guard
function workerReachable(cfg: TeamConfig, w: WorkerInfo): boolean {
return cfg.worker_launch_mode === 'prompt' ? isPromptWorkerAlive(cfg, w) : isWorkerAlive(cfg.tmux_session, w.index, w.pane_id, w.pid);
} Try / catch
try { await assignTask(t, id, w, cwd); } catch (e) { if ((e as Error).message === 'worker_notify_failed') { await restartWorker(t, w); await assignTask(t, id, w, cwd); } else throw e; } Prevention
- Health-check worker panes before dispatch
- Accept trust prompts when provisioning workers
- Auto-restart dead workers and re-dispatch (claim is released for you)
When it happens
Trigger: claimTask succeeds but every dispatch retry (up to 2, including retry delays for trust prompts) fails — e.g. worker pane is dead, tmux send-keys fails, or the agent session is unresponsive.
Common situations: Worker pane crashed or was killed between team start and assignment; tmux session/pane IDs stale after a server restart; trust prompt not accepted so dispatch retries exhaust.
Related errors
- ${label} must not be a symlink: ${path}
- Cannot use out-of-scope artifact path; ${evidenceDescription
- Cannot advance Autopilot from ralplan to ultragoal with forg
- Refusing to overwrite existing ${repoRelative(cwd, missionPa
- Unknown tmux-hook subcommand: ${subcommand}
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/172c4ebd485f4c46.
Report an issue: GitHub.