Yeachan-Heo/oh-my-codex · error · Error
canonical_scale_up_rollback_task_verification_failed
Error message
canonical_scale_up_rollback_task_verification_failed:${taskId} What it means
During scale-up rollback, a task created during the failed scale-up attempt still exists on disk and is not owned by an unresolved worker, meaning cleanup did not remove or reassign it. The message embeds the offending taskId.
Solutions
- Inspect the reported task ID under the team task store and delete or reassign it manually
- Re-run scaleUp (idempotent recovery reconciles leftover tasks) after ensuring no concurrent workers are writing
- Check/clear the membership task transaction journal if it is stuck
Defensive patterns
Strategy: retry
Try / catch
catch (e) {
const m = /canonical_scale_up_rollback_task_verification_failed:(.+)$/.exec(String((e as Error).message));
if (m) { await deleteTaskManually(team, m[1]); return retryScaleUp(); }
throw e;
} Prevention
- Quiesce worker task writes before scaling
- Treat createdTaskIds cleanup as idempotent and re-run scaleUp to reconcile
When it happens
Trigger: scaleUp failure path iterates createdTaskIds; readTask returns a task whose owner is not in unresolvedWorkerNames, so rollback left a stray task.
Common situations: Task files written concurrently by workers during rollback; interrupted cleanup (process killed mid-rollback); transaction journal desync.
Related errors
- canonical_scale_up_rollback_worker_verification_failed
- canonical_scale_up_rollback_resolved_membership_verification…
- canonical_scale_down_task_verification_failed
- canonical_scale_up_rollback_membership_verification_failed
- canonical_scale_up_rollback_tracking_verification_failed
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/856c1401e0954f49.
Report an issue: GitHub.
Appendix: source
Thrown at src/team/scaling.ts:986
const cleanupWorkerNames = new Set([
...rollbackWorkers
.filter((worker) => typeof worker.pane_id !== 'string' || resolvedPaneIds.has(worker.pane_id))
.map((worker) => worker.name),
...(context.workerName && !unresolvedWorkerNames.has(context.workerName) ? [context.workerName] : []),
]);
try {
for (const taskId of createdTaskIds) {
const task = await readTask(sanitized, taskId, leaderCwd);
if (task && unresolvedWorkerNames.has(task.owner ?? '')) continue;
await rm(join(teamStateRoot, 'team', sanitized, 'tasks', `task-${taskId}.json`), { force: true });
}
await Promise.all([...cleanupWorkerNames].map(async (workerName) => {
await rm(join(teamStateRoot, 'team', sanitized, 'workers', workerName), { recursive: true, force: true });
}));
for (const taskId of createdTaskIds) {
const task = await readTask(sanitized, taskId, leaderCwd);
if (task && unresolvedWorkerNames.has(task.owner ?? '')) continue;
if (task) throw new Error(`canonical_scale_up_rollback_task_verification_failed:${taskId}`);
}
for (const workerName of cleanupWorkerNames) {
if (existsSync(join(teamStateRoot, 'team', sanitized, 'workers', workerName))) {
throw new Error(`canonical_scale_up_rollback_worker_verification_failed:${workerName}`);
}
}
} catch (rollbackError) {
cleanupDebt.push(`canonical_cleanup_failed:${String(rollbackError)}`);
}
try {
const contextWorkerName = context.worker?.name ?? context.workerName;
const contextWorktreePath = context.worker?.worktree_path ?? context.worktreePath;
if (contextWorkerName && contextWorktreePath && !unresolvedWorkerNames.has(contextWorkerName)) {
await removeWorkerWorktreeRootAgentsFile(sanitized, contextWorkerName, teamStateRoot, contextWorktreePath);
}
const unresolvedWorktreePaths = new Set(rollbackWorkers
.filter((worker) => unresolvedWorkerNames.has(worker.name) && typeof worker.worktree_path === 'string')View on GitHub (pinned to 3ad79a8a6f)