{"record":{"id":"a1f754f15895d50d","repo":"shareAI-lab/learn-claude-code","slug":"invalid-task-status-task-status","errorCode":null,"errorMessage":"Invalid task status: {task.status}","messagePattern":"Invalid task status: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s10_task_system/code.py","lineNumber":143,"sourceCode":"                    json.dump(asdict(task), handle, indent=2)\n                return task\n            except FileExistsError:\n                continue\n        raise RuntimeError(\"Could not allocate a unique task ID\")\n\n    def save(self, task: Task) -> None:\n        self._path(task.id, create_root=True).write_text(\n            json.dumps(asdict(task), indent=2),\n            encoding=\"utf-8\",\n        )\n\n    def load(self, task_id: str) -> Task:\n        data = json.loads(self._path(task_id).read_text(encoding=\"utf-8\"))\n        task = Task(**data)\n        if task.id != task_id:\n            raise ValueError(f\"Task file ID does not match {task_id}\")\n        if task.status not in (\"pending\", \"in_progress\", \"completed\"):\n            raise ValueError(f\"Invalid task status: {task.status}\")\n        return task\n\n    def list(self) -> list[Task]:\n        if not self.directory.exists():\n            return []\n        root = self._root()\n        return [self.load(path.stem)\n                for path in sorted(root.glob(\"task_*.json\"))]\n\n\nTASKS = TaskStore(TASKS_DIR)\n\n\ndef create_task(subject: str, description: str = \"\",\n                blockedBy: list[str] | None = None) -> Task:\n    return TASKS.create(subject, description, blockedBy)\n\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s10_task_system/code.py#L125-L161","documentation":"Raised by TaskStore.load() in s10_task_system/code.py:143 when a task file's status field is anything other than 'pending', 'in_progress', or 'completed'. These three strings are the task lifecycle's closed vocabulary; the check runs after the ID-match check so the file is known to be well-located but its content is stale or hand-edited. Any other value — including old vocabulary like 'done', 'blocked', capitalized variants, or null — is rejected.","triggerScenarios":"Hand-editing .tasks/task_xxx.json and setting status to 'done'; loading a store written by an older/newer version that used a different status vocabulary; external scripts writing tasks with status 'blocked'; a JSON null or missing field surfacing as None. Task(**data) would already fail on a missing key with TypeError, so this raise specifically hits present-but-wrong values.","commonSituations":"Schema drift between versions of the task system; users 'finishing' tasks with a text editor; integration code that assumed an open-ended status enum.","solutions":["Map legacy statuses onto the allowed set when loading: 'done'/'finished' -> 'completed', 'active'/'running' -> 'in_progress', anything else -> 'pending', then save(task) to persist the corrected value.","Write status changes only through TaskStore.save() and the provided transition helpers so vocabulary stays closed.","If a new status is genuinely needed, extend the tuple in load() and the creation code together, and migrate existing files."],"exampleFix":"# before\ndata['status'] = 'done'\n\n# after\nSTATUS_MAP = {'done': 'completed', 'finished': 'completed',\n              'active': 'in_progress', 'running': 'in_progress'}\ndata['status'] = STATUS_MAP.get(data['status'], 'pending')","handlingStrategy":"fallback","validationCode":"ALLOWED = {'pending', 'in_progress', 'completed'}\n\ndef status_is_loadable(status) -> bool:\n    return status in ALLOWED","typeGuard":"def is_task_status(value) -> bool:\n    return value in ('pending', 'in_progress', 'completed')","tryCatchPattern":"try:\n    task = store.load(task_id)\nexcept ValueError as e:\n    if 'Invalid task status' in str(e):\n        legacy = {'done': 'completed', 'active': 'in_progress'}\n        task = patch_status(store, task_id, legacy)  # rewrite file, reload\n    else:\n        raise","preventionTips":["Update statuses only through TaskStore.save()/transition helpers.","When migrating stores, normalize legacy status strings to the three allowed values.","Add a store lint pass that validates every file with the same rules load() enforces."],"tags":["tasks","validation","enums","data-integrity"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}