FoundationAgents/OpenManus · warning · ToolError

Parameter `steps` must be a non-empty list of strings for co

Error message

Parameter `steps` must be a non-empty list of strings for command: create

What it means

Raised by _create_plan (app/tool/planning.py:140) when the steps argument for command='create' is None, an empty list, or not a list of strings (any non-str element fails the all() check). Steps drive step_statuses/step_notes arrays ('not_started' * len(steps)), so at least one string step is mandatory at creation.

Source

Thrown at app/tool/planning.py:140

    ) -> ToolResult:
        """Create a new plan with the given ID, title, and steps."""
        if not plan_id:
            raise ToolError("Parameter `plan_id` is required for command: create")

        if plan_id in self.plans:
            raise ToolError(
                f"A plan with ID '{plan_id}' already exists. Use 'update' to modify existing plans."
            )

        if not title:
            raise ToolError("Parameter `title` is required for command: create")

        if (
            not steps
            or not isinstance(steps, list)
            or not all(isinstance(step, str) for step in steps)
        ):
            raise ToolError(
                "Parameter `steps` must be a non-empty list of strings for command: create"
            )

        # Create a new plan with initialized step statuses
        plan = {
            "plan_id": plan_id,
            "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)}"
        )

View on GitHub (pinned to 52a13f2a57)

Solutions

  1. Pass a non-empty list of strings: steps=['Install deps', 'Run tests', 'Deploy'].
  2. If the model returned a single string, wrap/split it: steps=[s.strip() for s in raw.split('\n') if s.strip()].
  3. If you genuinely need an empty plan, create it with one placeholder step ('Define steps') and later update with the real list.

Example fix

// before
await planning.execute(command='create', plan_id='p1', title='T', steps='do things')

// after
await planning.execute(command='create', plan_id='p1', title='T', steps=['do things'])
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(steps, list) or not steps or not all(isinstance(s, str) and s.strip() for s in steps):
    raise ValueError('steps must be a non-empty list[str]')
await planning.execute(command='create', plan_id=pid, title=title, steps=steps)

Type guard

from typing import TypeGuard
def is_step_list(v: object) -> TypeGuard[list[str]]:
    return isinstance(v, list) and len(v) > 0 and all(isinstance(s, str) for s in v)

Try / catch

try:
    await planning.execute(command='create', plan_id=pid, title=t, steps=s)
except ToolError as e:
    if 'steps` must be a non-empty list' in str(e):
        s = [str(x).strip() for x in s if str(x).strip()] or ['Placeholder step']
        await planning.execute(command='create', plan_id=pid, title=t, steps=s)
    else:
        raise

Prevention

When it happens

Trigger: Calling create with steps omitted, steps=[], steps='step one' (a plain string, not a list), or steps=['a', 2] (mixed types). JSON configs that pass a dict or nested arrays also fail the isinstance checks.

Common situations: LLM tool calls that summarize steps as one string instead of a list; empty plans attempted as placeholders; steps parsed from YAML/JSON landing as tuples or dicts before reaching the tool.

Related errors


AI-assisted analysis of FoundationAgents/OpenManus@52a13f2a57 (2026-08-15). Data as JSON: /api/errors/74b5fb2e58515d5c. Report an issue: GitHub.