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
- 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"
- Treat the list as a cursor: finish/abandon one item before starting another
- 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
- Treat the todo list as a cursor: transition old in_progress -> completed in the same call that starts the next item
- Remember each update replaces the whole snapshot — stale statuses persist only if you restate them
- Never parallelize with two in_progress items on this harness
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
- Max 20 todos allowed
- Item {item_id}: text required
- Item {item_id}: invalid status '{status}'
- Invalid status: {status}
- Invalid status: {status}
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/409b625b3a63c27f.
Report an issue: GitHub.