{"record":{"id":"409b625b3a63c27f","repo":"shareAI-lab/learn-claude-code","slug":"only-one-task-can-be-in-progress-at-a-time","errorCode":null,"errorMessage":"Only one task can be in_progress at a time","messagePattern":"Only one task can be in_progress at a time","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"agents/s03_todo_write.py","lineNumber":73,"sourceCode":"\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)\n\n\nTODO = TodoManager()\n\n","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/agents/s03_todo_write.py#L55-L91","documentation":"Raised by TodoManager.update() in agents/s03_todo_write.py:73 after per-item validation passes, if more than one item carries status \"in_progress\". The manager enforces a single-active-task invariant: the todo list is a cursor, not a tracker, so exactly zero or one items may be in flight at a time. The check runs on the submitted snapshot, so the whole update is rejected and the previous list retained.","triggerScenarios":"The model marks item 3 in_progress while forgetting to flip item 2 (from a previous turn) back to completed/pending in the same snapshot. Or it optimistically starts two parallel workstreams and marks both in_progress in one call.","commonSituations":"Because update() replaces the full list, stale in_progress items from earlier turns survive into the new snapshot unless explicitly transitioned — this is the most frequent cause. Parallel-minded models on sequential harnesses.","solutions":["In each todo_update, set the old in_progress item to \"completed\" (or \"pending\") in the same call that marks the next one \"in_progress\"","Treat the list as a cursor: finish/abandon one item before starting another","Remember the snapshot rule — previous statuses do not carry over, you must restate every item with its intended current status"],"exampleFix":"# before\n[\n {\"id\": \"2\", \"text\": \"refactor db\", \"status\": \"in_progress\"},\n {\"id\": \"3\", \"text\": \"write tests\", \"status\": \"in_progress\"},\n]\n# ValueError: Only one task can be in_progress at a time\n\n# after\n[\n {\"id\": \"2\", \"text\": \"refactor db\", \"status\": \"completed\"},\n {\"id\": \"3\", \"text\": \"write tests\", \"status\": \"in_progress\"},\n]","handlingStrategy":"validation","validationCode":"statuses = [str(i.get(\"status\", \"pending\")).lower() for i in items]\nassert statuses.count(\"in_progress\") <= 1, \"demote the previous in_progress item to completed/pending in this snapshot\"\n# auto-fix variant:\n# seen = False\n# for i in items:\n#     if i.get(\"status\") == \"in_progress\":\n#         if seen: i[\"status\"] = \"pending\"\n#         else: seen = True","typeGuard":"def has_single_in_progress(items: list) -> bool:\n    return sum(1 for i in items if str(i.get(\"status\", \"\")).lower() == \"in_progress\") <= 1","tryCatchPattern":"try:\n    TODO.update(items)\nexcept ValueError as e:\n    if \"in_progress\" in str(e):\n        return \"Tool error: one in_progress max. Re-submit with the finished item marked completed first.\"\n    raise","preventionTips":["Treat the todo list as a cursor: transition old in_progress -> completed in the same call that starts the next item","Remember each update replaces the whole snapshot — stale statuses persist only if you restate them","Never parallelize with two in_progress items on this harness"],"tags":["validation","todo","state-machine","agent-tools","python"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}