shareAI-lab/learn-claude-code · warning · ValueError

Max 20 todos allowed

Error message

Max 20 todos allowed

What it means

Raised by TodoManager.update in s05_todo_write/code.py when the incoming todos list has more than 20 entries. The cap is checked after parsing/type validation but before per-item validation, so a 25-item list fails immediately. State is untouched: self.items keeps the previous value.

Source

Thrown at s05_todo_write/code.py:127

class TodoManager:
    def __init__(self):
        self.items: list[dict] = []

    def update(self, todos: list | str) -> str:
        if isinstance(todos, str):
            try:
                todos = json.loads(todos)
            except json.JSONDecodeError:
                try:
                    todos = ast.literal_eval(todos)
                except (SyntaxError, ValueError) as e:
                    raise ValueError("todos must be a list or JSON array string") from e

        if not isinstance(todos, list):
            raise ValueError("todos must be a list")
        if len(todos) > 20:
            raise ValueError("Max 20 todos allowed")

        validated = []
        in_progress_count = 0
        for index, todo in enumerate(todos):
            if not isinstance(todo, dict):
                raise ValueError(f"todos[{index}] must be an object")

            content = str(todo.get("content", "")).strip()
            status = str(todo.get("status", "pending")).lower()
            if not content:
                raise ValueError(f"todos[{index}] requires content")
            if status not in ("pending", "in_progress", "completed"):
                raise ValueError(f"todos[{index}] has invalid status '{status}'")
            if status == "in_progress":
                in_progress_count += 1
            validated.append({"content": content, "status": status})

        if in_progress_count > 1:

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Merge related steps to bring the count to 20 or fewer
  2. Replace the list each phase instead of growing it monotonically
  3. Enforce len(todos) <= 20 in the calling code or tool schema (maxItems: 20)

Example fix

// before
TODOS.update(all_steps)  # 24 items
// after
TODOS.update(all_steps[:20])  # or merge related steps first
Defensive patterns

Strategy: validation

Validate before calling

MAX = 20
if len(todos) > MAX:
    todos = todos[:MAX]  # or merge/drop consciously
assert len(todos) <= MAX
TODOS.update(todos)

Prevention

When it happens

Trigger: update() called with 21 or more todo dicts (or a JSON string deserializing to such a list).

Common situations: Fine-grained agent plans (one todo per file edited); importing long external checklists; appending instead of replacing when the list is already near the cap.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/a6efd994643ad89c. Report an issue: GitHub.