{"record":{"id":"2fa67fd9cb385b77","repo":"shareAI-lab/learn-claude-code","slug":"item-item-id-invalid-status-status","errorCode":null,"errorMessage":"Item {item_id}: invalid status '{status}'","messagePattern":"Item (.+?): invalid status '(.+?)'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agents/s03_todo_write.py","lineNumber":68,"sourceCode":"\n# -- TodoManager: structured state the LLM writes to --\nclass TodoManager:\n    def __init__(self):\n        self.items = []\n\n    def update(self, items: list) -> str:\n        if len(items) > 20:\n            raise ValueError(\"Max 20 todos allowed\")\n        validated = []\n        in_progress_count = 0\n        for i, item in enumerate(items):\n            text = str(item.get(\"text\", \"\")).strip()\n            status = str(item.get(\"status\", \"pending\")).lower()\n            item_id = str(item.get(\"id\", str(i + 1)))\n            if not text:\n                raise ValueError(f\"Item {item_id}: text required\")\n            if status not in (\"pending\", \"in_progress\", \"completed\"):\n                raise ValueError(f\"Item {item_id}: invalid status '{status}'\")\n            if status == \"in_progress\":\n                in_progress_count += 1\n            validated.append({\"id\": item_id, \"text\": text, \"status\": status})\n        if in_progress_count > 1:\n            raise ValueError(\"Only one task can be in_progress at a time\")\n        self.items = validated\n        return self.render()\n\n    def render(self) -> str:\n        if not self.items:\n            return \"No todos.\"\n        lines = []\n        for item in self.items:\n            marker = {\"pending\": \"[ ]\", \"in_progress\": \"[>]\", \"completed\": \"[x]\"}[item[\"status\"]]\n            lines.append(f\"{marker} #{item['id']}: {item['text']}\")\n        done = sum(1 for t in self.items if t[\"status\"] == \"completed\")\n        lines.append(f\"\\n({done}/{len(self.items)} completed)\")\n        return \"\\n\".join(lines)","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/agents/s03_todo_write.py#L50-L86","documentation":"Raised by TodoManager.update() in agents/s03_todo_write.py:68 when an item's `status` (lowercased, defaulted to \"pending\") is not one of the three allowed literals: pending, in_progress, completed. This is a closed-state-machine check — arbitrary progress labels like \"done\" or \"in-progress\" are rejected before the list is stored, so render()'s marker lookup (\"[ ]\", \"[>]\", \"[x]\") can never KeyError.","triggerScenarios":"The model sends status \"done\", \"Done\" is fine (lowercased) but \"finished\", \"in-progress\" (hyphen instead of underscore), \"in progress\" (space), or \"blocked\" all fail. A missing status defaults safely to pending, so the error only comes from a present-but-wrong value.","commonSituations":"\"done\" is the single most common LLM slip. Hyphen/space variants of in_progress. Models adding custom statuses like \"blocked\" or \"cancelled\" that the harness does not model.","solutions":["Use exactly one of: \"pending\", \"in_progress\", \"completed\" (lowercase, underscore in the middle)","Map model vocabulary to schema at the tool layer if you control the harness: normalize \"done\"->\"completed\", \"in-progress\"->\"in_progress\", or reject with a schema echo"],"exampleFix":"# before\n{\"id\": \"1\", \"text\": \"ship it\", \"status\": \"done\"}\n# ValueError: Item 1: invalid status 'done'\n\n# after\n{\"id\": \"1\", \"text\": \"ship it\", \"status\": \"completed\"}","handlingStrategy":"validation","validationCode":"ALLOWED = {\"pending\", \"in_progress\", \"completed\"}\nitems = [\n    {**i, \"status\": str(i.get(\"status\", \"pending\")).strip().lower().replace(\"-\", \"_\").replace(\" \", \"_\")}\n    for i in items\n]\nassert all(i[\"status\"] in ALLOWED for i in items), f\"bad status in {[i['status'] for i in items if i['status'] not in ALLOWED]}\"","typeGuard":"TODO_STATUSES = frozenset((\"pending\", \"in_progress\", \"completed\"))\n\ndef is_valid_todo_status(s: object) -> bool:\n    return isinstance(s, str) and s.lower() in TODO_STATUSES","tryCatchPattern":"try:\n    TODO.update(items)\nexcept ValueError as e:\n    if \"invalid status\" in str(e):\n        return f\"Tool error: {e}. Allowed: pending, in_progress, completed (lowercase, underscore).\"\n    raise","preventionTips":["Normalize status strings (strip, lower, hyphen/space -> underscore) before submitting","Beware 'done' — map it to 'completed' yourself","There is no blocked/cancelled state; model that via text, not status"],"tags":["validation","todo","state-machine","schema","python"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}