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

canonical_scale_down_task_verification_failed:${task.id}

Error message

canonical_scale_down_task_verification_failed:${task.id}

What it means

After scale-down, a reconciled task still references a removed worker as owner or claim owner, meaning task reassignment did not take effect. Message embeds the task.id.

Source

Thrown at src/team/scaling.ts:2174

              worktree_repo_root: worker.worktree_repo_root,
              worktree_branch: worker.worktree_branch,
              worktree_detached: worker.worktree_detached,
              worktree_created: worker.worktree_created,
              team_state_root: worker.team_state_root,
            })),
          };
          await writeAtomic(cleanupDebtPath, JSON.stringify(cleanupDebtBase, null, 2));
          await commitTeamMembershipTaskTransaction(sanitized, leaderCwd, membershipTransaction);
          const committed = await readTeamConfig(sanitized, leaderCwd);
          if (!committed || committed.workers.some((worker) => removableWorkerNames.has(worker.name))) {
            throw new Error('canonical_scale_down_config_verification_failed');
          }
          config.workers = committed.workers;
          config.worker_count = committed.worker_count;
          for (const task of reconciledTasks) {
            const reconciled = await readTask(sanitized, task.id, leaderCwd);
            if (reconciled && (removableWorkerNames.has(reconciled.owner ?? '') || removableWorkerNames.has(reconciled.claim?.owner ?? ''))) {
              throw new Error(`canonical_scale_down_task_verification_failed:${task.id}`);
            }
          }

          // PID-less rollback records are cleanup debt, not effect authority. A
          // fresh gone proof may resolve one, but a live or unavailable proof
          // must remain debt rather than letting teardown adopt its current PID.
          const pinnedPaneWorkers = removableWorkers.filter((worker): worker is WorkerInfo & {
            pane_id: string;
            pid: number;
          } => (
            typeof worker.pane_id === 'string'
            && /^%\d+$/.test(worker.pane_id)
            && typeof worker.pid === 'number'
            && Number.isSafeInteger(worker.pid)
            && worker.pid > 0
          ));
          const unpinnedPaneWorkers = removableWorkers.filter((worker) => (
            typeof worker.pane_id === 'string'

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the reported task file and manually clear/reassign the owner and claim fields
  2. Ensure the removed workers' processes are terminated before scaling down
  3. Re-run scaleDown to reconcile remaining task ownership
Defensive patterns

Strategy: retry

Try / catch

catch (e) {
  const m = /canonical_scale_down_task_verification_failed:(.+)$/.exec(String((e as Error).message));
  if (m) { await reassignTaskOwner(team, m[1]); return retryScaleDown(); }
  throw e;
}

Prevention

When it happens

Trigger: scaleDown re-reads each reconciled task; readTask returns a task whose owner or claim.owner is in removableWorkerNames.

Common situations: Concurrent worker writing/claiming its task during teardown; task file rewritten between reassignment and verification; transaction journal not applied to task files.

Related errors


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