usestrix/strix · error · TypeError

Each todo entry must be a string or object with a title

Error message

Each todo entry must be a string or object with a title

What it means

Thrown by the todo normalizer when an element inside the todos list is neither a string nor a dict. Strings are treated as bare titles; dicts carry title/description/priority. Any other element type (number, null, nested list) is rejected with this TypeError.

Source

Thrown at strix/tools/todo/tools.py:217

            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"),
            },
        )
    return normalized


def _apply_single_update(
    agent_todos: dict[str, dict[str, Any]],
    todo_id: str,
    title: str | None = None,
    description: str | None = None,

View on GitHub (pinned to 8551339130)

Solutions

  1. Make every list element either a plain title string or an object with a 'title' key
  2. Drop null/undefined entries before calling the tool
  3. Convert numeric entries to strings if they were meant as titles

Example fix

# before
todos = [null, "write tests"]

# after
todos = ["write tests"]
Defensive patterns

Strategy: type-guard

Validate before calling

entries = [e for e in todos if e is not None]
if not all(isinstance(e, (str, dict)) for e in entries):
    raise TypeError("todo entries must be str or dict")

Type guard

def is_todo_entry(v) -> bool:
    return isinstance(v, (str, dict))

Try / catch

try:
    tool.write_todos(entries)
except TypeError as e:
    if "string or object" in str(e):
        entries = [e for e in entries if isinstance(e, (str, dict))]
        tool.write_todos(entries)

Prevention

When it happens

Trigger: todos=[42, {'title': 'x'}], todos=[["a"]], or a JSON payload like '[null, "task"]' where an element parses to null.

Common situations: LLM emitting mixed-type arrays (numbers as task ids), JSON null elements from sparse arrays, or wrapping titles in an extra array layer.

Related errors


AI-assisted analysis of usestrix/strix@8551339130 (2026-08-15). Data as JSON: /api/errors/965c1bf25b1c7913. Report an issue: GitHub.