FoundationAgents/OpenManus · warning · ToolError
Parameter `plan_id` is required for command: update
Error message
Parameter `plan_id` is required for command: update
What it means
Raised by _update_plan (app/tool/planning.py:165) when command='update' is dispatched without a truthy plan_id. Unlike get/mark_step, update has no fallback to the currently active plan — the target plan must be identified explicitly. Validation happens before any mutation, so no partial writes occur.
Source
Thrown at app/tool/planning.py:165
"title": title,
"steps": steps,
"step_statuses": ["not_started"] * len(steps),
"step_notes": [""] * len(steps),
}
self.plans[plan_id] = plan
self._current_plan_id = plan_id # Set as active plan
return ToolResult(
output=f"Plan created successfully with ID: {plan_id}\n\n{self._format_plan(plan)}"
)
def _update_plan(
self, plan_id: Optional[str], title: Optional[str], steps: Optional[List[str]]
) -> ToolResult:
"""Update an existing plan with new title or steps."""
if not plan_id:
raise ToolError("Parameter `plan_id` is required for command: update")
if plan_id not in self.plans:
raise ToolError(f"No plan found with ID: {plan_id}")
plan = self.plans[plan_id]
if title:
plan["title"] = title
if steps:
if not isinstance(steps, list) or not all(
isinstance(step, str) for step in steps
):
raise ToolError(
"Parameter `steps` must be a list of strings for command: update"
)
# Preserve existing step statuses for unchanged stepsView on GitHub (pinned to 52a13f2a57)
Solutions
- Pass the plan_id explicitly: `planning.execute(command='update', plan_id='p1', title='New title')`.
- In wrappers, resolve the active plan yourself first (planning._current_plan_id or a prior list/get call) and inject it for update calls.
- Keep the asymmetry documented in your agent prompt: update requires plan_id; get/mark_step fall back to the active plan.
Example fix
// before await planning.execute(command='update', title='Revised') // after await planning.execute(command='update', plan_id='p1', title='Revised')
Defensive patterns
Strategy: validation
Validate before calling
plan_id = plan_id or current_active_id() # resolve in caller; update has no fallback
if not plan_id:
raise ValueError('update requires an explicit plan_id')
await planning.execute(command='update', plan_id=plan_id, title=title) Type guard
def is_plan_id(v: str | None) -> bool:
return isinstance(v, str) and bool(v.strip()) Try / catch
try:
await planning.execute(command='update', title=t)
except ToolError as e:
if 'plan_id` is required for command: update' in str(e):
await planning.execute(command='update', plan_id=await resolve_active_id(), title=t)
else:
raise Prevention
- Remember the asymmetry: update needs plan_id; get/mark_step fall back to the active plan.
- Inject plan_id at your wrapper layer for every update call.
When it happens
Trigger: Calling `planning.execute(command='update', title='New title')` with plan_id omitted or ''. This is easy to hit because the sibling commands get and mark_step do default to the active plan, suggesting (incorrectly) that update would too.
Common situations: Models switching between planning commands and assuming uniform optional plan_id semantics; refactors that copy a get call and change only the command string.
Related errors
- Parameter `plan_id` is required for command: create
- Parameter `title` is required for command: create
- Parameter `plan_id` is required for command: set_active
- No primary agent available
- Unrecognized command: {command}. Allowed commands are: creat
AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15).
Data as JSON: /api/errors/19c485383ef68bc4.
Report an issue: GitHub.