{"record":{"id":"a9705698e27a7270","repo":"usestrix/strix","slug":"each-todo-entry-must-include-a-non-empty-title","errorCode":null,"errorMessage":"Each todo entry must include a non-empty 'title'","messagePattern":"Each todo entry must include a non-empty 'title'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"strix/tools/todo/tools.py","lineNumber":220,"sourceCode":"            return [{\"title\": entry} for entry in entries]\n\n    if isinstance(data, dict):\n        data = [data]\n    if not isinstance(data, list):\n        raise TypeError(\"Todos must be provided as a list, dict, or JSON string\")\n\n    normalized: list[dict[str, Any]] = []\n    for item in data:\n        if isinstance(item, str):\n            title = item.strip()\n            if title:\n                normalized.append({\"title\": title})\n            continue\n        if not isinstance(item, dict):\n            raise TypeError(\"Each todo entry must be a string or object with a title\")\n        title = item.get(\"title\", \"\")\n        if not isinstance(title, str) or not title.strip():\n            raise ValueError(\"Each todo entry must include a non-empty 'title'\")\n        normalized.append(\n            {\n                \"title\": title.strip(),\n                \"description\": (item.get(\"description\") or \"\").strip() or None,\n                \"priority\": item.get(\"priority\"),\n            },\n        )\n    return normalized\n\n\ndef _apply_single_update(\n    agent_todos: dict[str, dict[str, Any]],\n    todo_id: str,\n    title: str | None = None,\n    description: str | None = None,\n    priority: str | None = None,\n    status: str | None = None,\n) -> dict[str, Any] | None:","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/tools/todo/tools.py#L202-L238","documentation":"Thrown when a dict-shaped todo entry has a 'title' that is missing, not a string, or blank after stripping. The title is the only required field per entry; description and priority are optional. Empty string entries from bare strings are silently skipped, but dict entries with empty titles are hard errors.","triggerScenarios":"todos=[{'description': 'no title'}], todos=[{'title': ''}], todos=[{'title': 42}] (non-string title), or a title of only whitespace.","commonSituations":"Agent moving the task text into 'description' and leaving 'title' empty; templating that renders an unset variable as ''; numbers or ids mistakenly placed in title.","solutions":["Give every todo object a non-empty string 'title': {'title': 'write tests', 'description': '...'}","If the text landed in 'description', move it to 'title'","Strip whitespace when generating titles programmatically and skip blanks"],"exampleFix":"# before\ntodos = [{\"description\": \"refactor auth module\"}]\n\n# after\ntodos = [{\"title\": \"refactor auth module\"}]","handlingStrategy":"validation","validationCode":"for t in todos:\n    if isinstance(t, dict):\n        title = t.get(\"title\")\n        assert isinstance(title, str) and title.strip(), f\"bad title in {t!r}\"","typeGuard":"def has_valid_title(t) -> bool:\n    if isinstance(t, str):\n        return bool(t.strip())\n    return isinstance(t, dict) and isinstance(t.get(\"title\"), str) and bool(t[\"title\"].strip())","tryCatchPattern":"try:\n    tool.write_todos(todos)\nexcept ValueError as e:\n    if \"title\" in str(e):\n        todos = [t if isinstance(t, str) else {**t, \"title\": t.get(\"title\") or t.get(\"description\") or \"untitled\"} for t in todos]\n        tool.write_todos(todos)","preventionTips":["Fall back description -> title when preparing payloads","Trim and skip blank titles during generation instead of letting the tool reject them"],"tags":["validation","todo-tool","required-field"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}