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

Only one task can be in_progress at a time

Error message

Only one task can be in_progress at a time

What it means

Raised by TodoManager.update() in agents/s03_todo_write.py:73 after per-item validation passes, if more than one item carries status "in_progress". The manager enforces a single-active-task invariant: the todo list is a cursor, not a tracker, so exactly zero or one items may be in flight at a time. The check runs on the submitted snapshot, so the whole update is rejected and the previous list retained.

Source

Thrown at agents/s03_todo_write.py:73

    def update(self, items: list) -> str:
        if len(items) > 20:
            raise ValueError("Max 20 todos allowed")
        validated = []
        in_progress_count = 0
        for i, item in enumerate(items):
            text = str(item.get("text", "")).strip()
            status = str(item.get("status", "pending")).lower()
            item_id = str(item.get("id", str(i + 1)))
            if not text:
                raise ValueError(f"Item {item_id}: text required")
            if status not in ("pending", "in_progress", "completed"):
                raise ValueError(f"Item {item_id}: invalid status '{status}'")
            if status == "in_progress":
                in_progress_count += 1
            validated.append({"id": item_id, "text": text, "status": status})
        if in_progress_count > 1:
            raise ValueError("Only one task 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 item in self.items:
            marker = {"pending": "[ ]", "in_progress": "[>]", "completed": "[x]"}[item["status"]]
            lines.append(f"{marker} #{item['id']}: {item['text']}")
        done = sum(1 for t in self.items if t["status"] == "completed")
        lines.append(f"\n({done}/{len(self.items)} completed)")
        return "\n".join(lines)


TODO = TodoManager()

View on GitHub (pinned to 985456f4ad)

Solutions

  1. In each todo_update, set the old in_progress item to "completed" (or "pending") in the same call that marks the next one "in_progress"
  2. Treat the list as a cursor: finish/abandon one item before starting another
  3. Remember the snapshot rule — previous statuses do not carry over, you must restate every item with its intended current status

Example fix

# before
[
 {"id": "2", "text": "refactor db", "status": "in_progress"},
 {"id": "3", "text": "write tests", "status": "in_progress"},
]
# ValueError: Only one task can be in_progress at a time

# after
[
 {"id": "2", "text": "refactor db", "status": "completed"},
 {"id": "3", "text": "write tests", "status": "in_progress"},
]
Defensive patterns

Strategy: validation

Validate before calling

statuses = [str(i.get("status", "pending")).lower() for i in items]
assert statuses.count("in_progress") <= 1, "demote the previous in_progress item to completed/pending in this snapshot"
# auto-fix variant:
# seen = False
# for i in items:
#     if i.get("status") == "in_progress":
#         if seen: i["status"] = "pending"
#         else: seen = True

Type guard

def has_single_in_progress(items: list) -> bool:
    return sum(1 for i in items if str(i.get("status", "")).lower() == "in_progress") <= 1

Try / catch

try:
    TODO.update(items)
except ValueError as e:
    if "in_progress" in str(e):
        return "Tool error: one in_progress max. Re-submit with the finished item marked completed first."
    raise

Prevention

When it happens

Trigger: The model marks item 3 in_progress while forgetting to flip item 2 (from a previous turn) back to completed/pending in the same snapshot. Or it optimistically starts two parallel workstreams and marks both in_progress in one call.

Common situations: Because update() replaces the full list, stale in_progress items from earlier turns survive into the new snapshot unless explicitly transitioned — this is the most frequent cause. Parallel-minded models on sequential harnesses.

Related errors


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