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

todos[{index}] has invalid status '{status}'

Error message

todos[{index}] has invalid status '{status}'

What it means

Raised by TodoManager.update in s05_todo_write/code.py when a todo's status (string-coerced and lowercased) is not pending, in_progress, or completed. The invalid value is echoed in the message. Statuses are normalized with str().lower(), so casing is forgiven but any other wording, separator style, or non-string type fails.

Source

Thrown at s05_todo_write/code.py:140

                    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:
            raise ValueError("Only one todo can be in_progress at a time")

        self.items = validated
        return self.render()

    def render(self) -> str:
        if not self.items:
            return "No todos."

        lines = []
        for todo in self.items:
            marker = {
                "pending": "[ ]",

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Map external statuses to the allowed trio before calling (done->completed, doing->in_progress)
  2. Validate status in ('pending','in_progress','completed') client-side and correct early
  3. Remember casing is tolerated but the exact snake_case words are required

Example fix

// before
{"content": "ship", "status": "done"}
// after
{"content": "ship", "status": "completed"}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = ('pending', 'in_progress', 'completed')
MAP = {'done': 'completed', 'todo': 'pending', 'doing': 'in_progress'}
for t in todos:
    s = str(t.get('status', 'pending')).lower()
    t['status'] = MAP.get(s, s)
assert all(t['status'] in ALLOWED for t in todos)
TODOS.update(todos)

Type guard

def is_allowed_status(s: object) -> bool:
    return str(s).lower() in ('pending', 'in_progress', 'completed')

Try / catch

try:
    TODOS.update(todos)
except ValueError as e:
    m = re.search(r"invalid status '(.*?)'", str(e))
    if m and m.group(1) in MAP:
        fixed = MAP[m.group(1)]
        todos = [{**t, 'status': fixed} if str(t['status']).lower() == m.group(1) else t for t in todos]
        TODOS.update(todos)
    else:
        raise

Prevention

When it happens

Trigger: status: "done", "in-progress", "inprogress", "blocked", "todo", True (coerces to "true"), or 1 (coerces to "1").

Common situations: Agents using natural words like done/blocked; camelCase "inProgress" lowercasing to "inprogress" which still fails; statuses imported from an external tracker with a richer enum.

Related errors


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