Yeachan-Heo/oh-my-codex · error · Error
plan_approval_required
Error message
plan_approval_required
What it means
Thrown when governance requires plan approval and the task is marked requires_code_change === true, but isTaskApprovedForExecution reports the task has not been approved. The gate prevents unreviewed code-changing work from being dispatched.
Source
Thrown at src/team/runtime.ts:4700
const sanitized = sanitizeTeamName(teamName);
return await withTeamTaskMembershipBarrier(sanitized, cwd, async () => {
const phaseState = await readTeamPhaseState(sanitized, cwd);
if (phaseState?.terminal_epoch || (phaseState && isTerminalPhase(phaseState.current_phase))) {
throw new Error(teamContinuationRequiredDiagnostic(phaseState));
}
const task = await readTask(sanitized, taskId, cwd);
if (!task) throw new Error(`Task ${taskId} not found`);
const manifest = await readTeamManifestV2(sanitized, cwd);
const governance = resolveGovernancePolicy(manifest?.governance);
if (governance.delegation_only && workerName === 'leader-fixed') {
throw new Error('delegation_only_violation');
}
if (governance.plan_approval_required && task.requires_code_change === true) {
const approved = await isTaskApprovedForExecution(sanitized, taskId, cwd);
if (!approved) {
throw new Error('plan_approval_required');
}
}
const config = await readTeamConfig(sanitized, cwd);
if (!config) throw new Error(`Team ${sanitized} not found`);
const workerInfo = config.workers.find(w => w.name === workerName);
if (!workerInfo) throw new Error(`Worker ${workerName} not found in team`);
const dispatchPolicy = resolveDispatchPolicy(manifest?.policy, config.worker_launch_mode);
const claim = await claimTask(sanitized, taskId, workerName, task.version ?? 1, cwd);
if (!claim.ok) {
if (claim.error === 'blocked_dependency') {
throw new Error(`blocked_dependency:${(claim.dependencies ?? []).join(',')}`);
}
throw new Error(claim.error);
}
try {
// Retry dispatch up to 2 times to handle trust prompts during assignment (fixes #393).View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Approve the task plan first via the team's approval flow, then re-dispatch
- If approval genuinely is not needed for this task, clear requires_code_change on the task definition
- If governance is too strict, adjust plan_approval_required in the manifest governance policy
Example fix
// before
await assignTask(team, taskId, worker, cwd);
// after
if (task.requires_code_change) {
await approveTaskForExecution(team, taskId, cwd); // or CLI approval command
}
await assignTask(team, taskId, worker, cwd); Defensive patterns
Strategy: validation
Validate before calling
if (task.requires_code_change && governance.plan_approval_required) {
const ok = await isTaskApprovedForExecution(teamName, taskId, cwd);
if (!ok) throw new Error('approve the plan before dispatch');
} Try / catch
try { await assignTask(t, id, w, cwd); } catch (e) { if ((e as Error).message === 'plan_approval_required') { await approveTask(t, id); await assignTask(t, id, w, cwd); } else throw e; } Prevention
- Make approval part of the CI pipeline for code-changing tasks
- Keep task.requires_code_change accurate
- Audit governance flags after upgrades
When it happens
Trigger: Assigning a requires_code_change task under a manifest whose governance sets plan_approval_required, when no approval record exists for the task (isTaskApprovedForExecution returns false).
Common situations: Automated pipelines dispatching tasks straight after creation without the approval step; approvals stored per-team that were lost when state was reset; governance defaults changing between versions so previously-fine dispatch now requires approval.
Related errors
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/39ca864be4e2657e.
Report an issue: GitHub.