Yeachan-Heo/oh-my-codex · error · Error

canonical_scale_up_rollback_task_verification_failed:${taskI

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.

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)

Solutions

  1. Inspect the reported task ID under the team task store and delete or reassign it manually
  2. Re-run scaleUp (idempotent recovery reconciles leftover tasks) after ensuring no concurrent workers are writing
  3. 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

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


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/856c1401e0954f49. Report an issue: GitHub.