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

Only one todo can be in_progress at a time

Error message

Only one todo can be in_progress at a time

What it means

Raised by TodoManager.update in s05_todo_write/code.py when more than one element has status in_progress after per-item validation. The module enforces a single-active-item invariant so the rendered todo list has exactly one current focus. The count is tallied across the whole submitted list (which replaces, not merges with, existing items), so the error reflects the new list alone.

Source

Thrown at s05_todo_write/code.py:146

        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": "[ ]",
                "in_progress": "[>]",
                "completed": "[x]",
            }[todo["status"]]
            lines.append(f"{marker} {todo['content']}")

        done = sum(todo["status"] == "completed" for todo in self.items)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Before marking item B in_progress, set item A to completed (or pending) in the same submitted list
  2. Keep a helper that enforces exactly one in_progress before calling update
  3. Treat the invariant as intentional: structure plans as a serial focus, not parallel tracks

Example fix

// before
[{"content": "a", "status": "in_progress"}, {"content": "b", "status": "in_progress"}]
// after
[{"content": "a", "status": "completed"}, {"content": "b", "status": "in_progress"}]
Defensive patterns

Strategy: validation

Validate before calling

def enforce_single_active(todos):
    seen = False
    out = []
    for t in todos:
        s = str(t.get('status', 'pending')).lower()
        if s == 'in_progress':
            s = 'completed' if seen else 'in_progress'
            seen = True
        out.append({**t, 'status': s})
    return out

TODOS.update(enforce_single_active(todos))

Type guard

def single_in_progress(todos: list) -> bool:
    return sum(1 for t in todos if str(t.get('status', '')).lower() == 'in_progress') <= 1

Prevention

When it happens

Trigger: Submitting [{..."in_progress"}, {..."in_progress"}] — e.g. marking a second item active without first demoting the previous one to pending/completed in the same replacement list.

Common situations: Agents starting a new step while forgetting to close the previous one; parallel-work plans where the model wants two active items; appending an in_progress item to a list that already contains one.

Related errors


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