FoundationAgents/OpenManus · warning · ToolError
Parameter `steps` must be a list of strings for command: upd
Error message
Parameter `steps` must be a list of strings for command: update
What it means
Raised by _update_plan (app/tool/planning.py:179) when the steps argument is provided (truthy) but is not a list of strings. Note the difference from create: empty/None steps is fine on update (it just leaves steps unchanged); only a truthy, wrongly-typed steps value is rejected. Validation fires before statuses/notes are recomputed, so the existing plan is untouched.
Source
Thrown at app/tool/planning.py:179
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 steps
old_steps = plan["steps"]
old_statuses = plan["step_statuses"]
old_notes = plan["step_notes"]
# Create new step statuses and notes
new_statuses = []
new_notes = []
for i, step in enumerate(steps):
# If the step exists at the same position in old steps, preserve status and notes
if i < len(old_steps) and step == old_steps[i]:
new_statuses.append(old_statuses[i])
new_notes.append(old_notes[i])
else:View on GitHub (pinned to 52a13f2a57)
Solutions
- Send a plain list of str: steps=['Install', 'Test'].
- Omit steps entirely (or pass None) when only updating the title.
- Sanitize at the boundary: steps = [str(s) for s in steps] if steps is not None else None, dropping non-serializable entries before the call.
Example fix
// before await planning.execute(command='update', plan_id='p1', steps='Install, Test') // after await planning.execute(command='update', plan_id='p1', steps=['Install', 'Test'])
Defensive patterns
Strategy: type-guard
Validate before calling
if steps is not None:
steps = [str(s) for s in steps if isinstance(s, str) and s.strip()] or None
await planning.execute(command='update', plan_id=plan_id, title=title, steps=steps) Type guard
from typing import TypeGuard
def is_optional_step_list(v: object) -> TypeGuard[list[str] | None]:
return v is None or (isinstance(v, list) and all(isinstance(s, str) for s in v)) Try / catch
try:
await planning.execute(command='update', plan_id=pid, steps=s)
except ToolError as e:
if 'steps` must be a list of strings' in str(e):
await planning.execute(command='update', plan_id=pid, steps=[str(x) for x in s])
else:
raise Prevention
- Omit steps on update when only the title changes — None/empty is accepted.
- Sanitize to list[str] at the boundary; drop or stringify non-str entries before the call.
When it happens
Trigger: Calling update with steps='a, b, c' (string), steps=['x', None], steps=('x','y') (tuple from some parsers is actually a list-compatible failure only if elements are non-str; tuples fail isinstance(list)), or a dict of step->status pairs.
Common situations: LLM callers formatting steps as a comma-separated string; steps sourced from JSON that contains numbers or nulls; reusable code paths shared with create where the stricter non-empty rule trained users to always send steps, sometimes in the wrong shape.
Related errors
- Parameter `steps` must be a non-empty list of strings for co
- No primary agent available
- Unrecognized command: {command}. Allowed commands are: creat
- Parameter `plan_id` is required for command: create
- A plan with ID '{plan_id}' already exists. Use 'update' to m
AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15).
Data as JSON: /api/errors/a01676afa48089ce.
Report an issue: GitHub.