{"record":{"id":"a01676afa48089ce","repo":"FoundationAgents/OpenManus","slug":"parameter-steps-must-be-a-list-of-strings-for-co","errorCode":null,"errorMessage":"Parameter `steps` must be a list of strings for command: update","messagePattern":"Parameter `steps` must be a list of strings for command: update","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"warning","filePath":"app/tool/planning.py","lineNumber":179,"sourceCode":"        self, plan_id: Optional[str], title: Optional[str], steps: Optional[List[str]]\n    ) -> ToolResult:\n        \"\"\"Update an existing plan with new title or steps.\"\"\"\n        if not plan_id:\n            raise ToolError(\"Parameter `plan_id` is required for command: update\")\n\n        if plan_id not in self.plans:\n            raise ToolError(f\"No plan found with ID: {plan_id}\")\n\n        plan = self.plans[plan_id]\n\n        if title:\n            plan[\"title\"] = title\n\n        if steps:\n            if not isinstance(steps, list) or not all(\n                isinstance(step, str) for step in steps\n            ):\n                raise ToolError(\n                    \"Parameter `steps` must be a list of strings for command: update\"\n                )\n\n            # Preserve existing step statuses for unchanged steps\n            old_steps = plan[\"steps\"]\n            old_statuses = plan[\"step_statuses\"]\n            old_notes = plan[\"step_notes\"]\n\n            # Create new step statuses and notes\n            new_statuses = []\n            new_notes = []\n\n            for i, step in enumerate(steps):\n                # If the step exists at the same position in old steps, preserve status and notes\n                if i < len(old_steps) and step == old_steps[i]:\n                    new_statuses.append(old_statuses[i])\n                    new_notes.append(old_notes[i])\n                else:","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/FoundationAgents/OpenManus/blob/52a13f2a57d8c7f6737eefb02ccf569594d44273/app/tool/planning.py#L161-L197","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait planning.execute(command='update', plan_id='p1', steps='Install, Test')\n\n// after\nawait planning.execute(command='update', plan_id='p1', steps=['Install', 'Test'])","handlingStrategy":"type-guard","validationCode":"if steps is not None:\n    steps = [str(s) for s in steps if isinstance(s, str) and s.strip()] or None\nawait planning.execute(command='update', plan_id=plan_id, title=title, steps=steps)","typeGuard":"from typing import TypeGuard\ndef is_optional_step_list(v: object) -> TypeGuard[list[str] | None]:\n    return v is None or (isinstance(v, list) and all(isinstance(s, str) for s in v))","tryCatchPattern":"try:\n    await planning.execute(command='update', plan_id=pid, steps=s)\nexcept ToolError as e:\n    if 'steps` must be a list of strings' in str(e):\n        await planning.execute(command='update', plan_id=pid, steps=[str(x) for x in s])\n    else:\n        raise","preventionTips":["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."],"tags":["planning","validation","type-error"],"backgroundTag":null,"analyzedSha":"52a13f2a57d8c7f6737eefb02ccf569594d44273","analyzedAt":"2026-08-15T02:33:49.993Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}