{"record":{"id":"3c59707750b74cd0","repo":"usestrix/strix","slug":"todos-must-be-provided-as-a-list-dict-or-json-st","errorCode":null,"errorMessage":"Todos must be provided as a list, dict, or JSON string","messagePattern":"Todos must be provided as a list, dict, or JSON string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"strix/tools/todo/tools.py","lineNumber":207,"sourceCode":"\ndef _normalize_bulk_todos(raw_todos: Any) -> list[dict[str, Any]]:\n    if raw_todos is None:\n        return []\n    data: Any = raw_todos\n    if isinstance(raw_todos, str):\n        stripped = raw_todos.strip()\n        if not stripped:\n            return []\n        try:\n            data = json.loads(stripped)\n        except json.JSONDecodeError:\n            entries = [line.strip(\" -*\\t\") for line in stripped.splitlines() if line.strip(\" -*\\t\")]\n            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\"),","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/tools/todo/tools.py#L189-L225","documentation":"Thrown when the raw todos argument passed to Strix's todo tool is not a list, dict, or JSON string after parsing. Strings are first JSON-parsed (with a markdown-bullet fallback); dicts are wrapped into a single-element list. Anything else (int, None, tuple, nested non-list) hits this TypeError.","triggerScenarios":"Passing todos as a JSON-encoded number ('42'), a bare None after JSON-parsing, a tuple of dicts, or a JSON string like '\"just a title\"' that parses to a scalar string rather than a list.","commonSituations":"An agent wrapping a single string in extra quotes so json.loads returns a str; passing a generator/iterator that lost list-ness; double-encoding the payload (json.dumps applied twice).","solutions":["Pass a JSON array of objects: '[{\"title\": \"buy milk\"}]' or a plain Python list","A single dict is fine — it is auto-wrapped into a one-item list","For plain text, use markdown bullets ('- task one') which the line-splitter fallback accepts"],"exampleFix":"# before\ntodos = json.dumps(json.dumps([{\"title\": \"x\"}]))  # double-encoded\n\n# after\ntodos = json.dumps([{\"title\": \"x\"}])","handlingStrategy":"validation","validationCode":"import json\n\ndef normalize_todos(raw):\n    if isinstance(raw, str):\n        raw = json.loads(raw)\n    if isinstance(raw, dict):\n        raw = [raw]\n    if not isinstance(raw, list):\n        raise TypeError(\"expected list/dict/JSON string\")\n    return raw","typeGuard":"def is_todos_payload(v) -> bool:\n    return isinstance(v, (list, dict)) or (isinstance(v, str) and v.lstrip().startswith((\"[\", \"{\", \"-\")))","tryCatchPattern":"try:\n    tool.write_todos(raw)\nexcept TypeError as e:\n    if \"list, dict, or JSON string\" in str(e):\n        log raw payload type and re-serialize the source data as a JSON array","preventionTips":["Serialize with json.dumps exactly once","When building from another structure, coerce dicts into [dict] before sending","Prefer passing a real Python list over a JSON string when calling in-process"],"tags":["validation","todo-tool","json"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}