{"record":{"id":"e46f0c03e7e2dccf","repo":"shareAI-lab/learn-claude-code","slug":"invalid-task-id-task-id-r","errorCode":null,"errorMessage":"Invalid task ID: {task_id!r}","messagePattern":"Invalid task ID: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s10_task_system/code.py","lineNumber":90,"sourceCode":"    owner: str | None\n    blockedBy: list[str]\n\n\nclass TaskStore:\n    def __init__(self, directory: Path):\n        self.directory = directory\n\n    def _root(self, create: bool = False) -> Path:\n        if create:\n            self.directory.mkdir(parents=True, exist_ok=True)\n        root = self.directory.resolve()\n        if not root.is_relative_to(WORKDIR.resolve()):\n            raise ValueError(\"Task store escapes the workspace\")\n        return root\n\n    def _path(self, task_id: str, create_root: bool = False) -> Path:\n        if not isinstance(task_id, str) or not TASK_ID_PATTERN.fullmatch(task_id):\n            raise ValueError(f\"Invalid task ID: {task_id!r}\")\n        root = self._root(create=create_root)\n        path = (root / f\"{task_id}.json\").resolve()\n        if not path.is_relative_to(root):\n            raise ValueError(f\"Invalid task ID: {task_id!r}\")\n        return path\n\n    def exists(self, task_id: str) -> bool:\n        return self._path(task_id).is_file()\n\n    def create(self, subject: str, description: str = \"\",\n               blocked_by: list[str] | None = None) -> Task:\n        subject = subject.strip()\n        if not subject:\n            raise ValueError(\"Task subject cannot be empty\")\n\n        dependencies = list(dict.fromkeys(blocked_by or []))\n        for dependency in dependencies:\n            if not self.exists(dependency):","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s10_task_system/code.py#L72-L108","documentation":"Raised by TaskStore._path() in s10_task_system/code.py:90 when task_id is not a string or does not fully match TASK_ID_PATTERN, r'^task_[0-9a-f]{8}$' — exactly the literal prefix 'task_' followed by 8 lowercase hexadecimal characters. All store APIs (exists, load, save, delete, transition helpers) build the file path through this gate, so any malformed ID is rejected before touching the filesystem. The {!r} in the message shows the offending value with its Python repr.","triggerScenarios":"Calling store.load('42'), store.load('task_ABCD1234') (uppercase hex), 'task_12345' (too short), 'task_12345678g' (non-hex 'g'), a value with whitespace or a path suffix, or a non-string like None or an int. IDs originate from secrets.token_hex(4) at creation, so hand-typed or externally imported IDs are the usual offenders.","commonSituations":"Hardcoding IDs from another store or an older format; passing a UI-provided or LLM-provided identifier without normalization; copy/paste typos including the leading/trailing spaces or uppercase; feeding a bg_ background-task ID (s11) into the task store by mistake.","solutions":["Only use IDs returned by TaskStore.create(), which are always 'task_' + 8 lowercase hex chars.","Normalize before calling: strip whitespace and lowercase the hex portion, then re-check the pattern.","If you import external IDs, maintain a mapping table to freshly created task_ IDs instead of forcing foreign formats through the API."],"exampleFix":"# before\nstore.load('TASK_ab12cd34')\n\n# after\nimport re\nTASK_ID = re.compile(r'^task_[0-9a-f]{8}$')\ntid = task_id.strip().lower()\nif isinstance(task_id, str) and TASK_ID.fullmatch(tid):\n    task = store.load(tid)\nelse:\n    raise ValueError(f'not a valid task id: {task_id!r}')","handlingStrategy":"type-guard","validationCode":"import re\nfrom s10_task_system.code import TASK_ID_PATTERN\n\ndef valid_task_id(task_id) -> bool:\n    return isinstance(task_id, str) and bool(TASK_ID_PATTERN.fullmatch(task_id))","typeGuard":"from typing import Any\nimport re\n_TASK_ID = re.compile(r'^task_[0-9a-f]{8}$')\n\ndef is_task_id(value: Any) -> bool:\n    return isinstance(value, str) and bool(_TASK_ID.fullmatch(value))","tryCatchPattern":"try:\n    task = store.load(task_id)\nexcept ValueError as e:\n    if 'Invalid task ID' in str(e):\n        return None  # treat as not-found; log the repr for debugging\n    raise","preventionTips":["Only use IDs returned by create(); never hand-compose them.","Normalize incoming IDs: value.strip().lower() before pattern-checking.","Don't mix ID vocabularies (task_ vs bg_ vs cron_) across subsystems."],"tags":["validation","tasks","ids","regex"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}