usestrix/strix · error · TypeError
Todos must be provided as a list, dict, or JSON string
Error message
Todos must be provided as a list, dict, or JSON string
What it means
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.
Source
Thrown at strix/tools/todo/tools.py:207
def _normalize_bulk_todos(raw_todos: Any) -> list[dict[str, Any]]:
if raw_todos is None:
return []
data: Any = raw_todos
if isinstance(raw_todos, str):
stripped = raw_todos.strip()
if not stripped:
return []
try:
data = json.loads(stripped)
except json.JSONDecodeError:
entries = [line.strip(" -*\t") for line in stripped.splitlines() if line.strip(" -*\t")]
return [{"title": entry} for entry in entries]
if isinstance(data, dict):
data = [data]
if not isinstance(data, list):
raise TypeError("Todos must be provided as a list, dict, or JSON string")
normalized: list[dict[str, Any]] = []
for item in data:
if isinstance(item, str):
title = item.strip()
if title:
normalized.append({"title": title})
continue
if not isinstance(item, dict):
raise TypeError("Each todo entry must be a string or object with a title")
title = item.get("title", "")
if not isinstance(title, str) or not title.strip():
raise ValueError("Each todo entry must include a non-empty 'title'")
normalized.append(
{
"title": title.strip(),
"description": (item.get("description") or "").strip() or None,
"priority": item.get("priority"),View on GitHub (pinned to 8551339130)
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
Example fix
# before
todos = json.dumps(json.dumps([{"title": "x"}])) # double-encoded
# after
todos = json.dumps([{"title": "x"}]) Defensive patterns
Strategy: validation
Validate before calling
import json
def normalize_todos(raw):
if isinstance(raw, str):
raw = json.loads(raw)
if isinstance(raw, dict):
raw = [raw]
if not isinstance(raw, list):
raise TypeError("expected list/dict/JSON string")
return raw Type guard
def is_todos_payload(v) -> bool:
return isinstance(v, (list, dict)) or (isinstance(v, str) and v.lstrip().startswith(("[", "{", "-"))) Try / catch
try:
tool.write_todos(raw)
except TypeError as e:
if "list, dict, or JSON string" in str(e):
log raw payload type and re-serialize the source data as a JSON array Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Related errors
- Each todo entry must be a string or object with a title
- Updates must be valid JSON
- Updates must be a list of update objects
- Each update must be an object with todo_id
- Each update must include 'todo_id'
AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15).
Data as JSON: /api/errors/3c59707750b74cd0.
Report an issue: GitHub.