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

Item {item_id}: invalid status '{status}'

Error message

Item {item_id}: invalid status '{status}'

What it means

Raised by TodoManager.update() in agents/s03_todo_write.py:68 when an item's `status` (lowercased, defaulted to "pending") is not one of the three allowed literals: pending, in_progress, completed. This is a closed-state-machine check — arbitrary progress labels like "done" or "in-progress" are rejected before the list is stored, so render()'s marker lookup ("[ ]", "[>]", "[x]") can never KeyError.

Source

Thrown at agents/s03_todo_write.py:68

# -- TodoManager: structured state the LLM writes to --
class TodoManager:
    def __init__(self):
        self.items = []

    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)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Use exactly one of: "pending", "in_progress", "completed" (lowercase, underscore in the middle)
  2. Map model vocabulary to schema at the tool layer if you control the harness: normalize "done"->"completed", "in-progress"->"in_progress", or reject with a schema echo

Example fix

# before
{"id": "1", "text": "ship it", "status": "done"}
# ValueError: Item 1: invalid status 'done'

# after
{"id": "1", "text": "ship it", "status": "completed"}
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {"pending", "in_progress", "completed"}
items = [
    {**i, "status": str(i.get("status", "pending")).strip().lower().replace("-", "_").replace(" ", "_")}
    for i in items
]
assert all(i["status"] in ALLOWED for i in items), f"bad status in {[i['status'] for i in items if i['status'] not in ALLOWED]}"

Type guard

TODO_STATUSES = frozenset(("pending", "in_progress", "completed"))

def is_valid_todo_status(s: object) -> bool:
    return isinstance(s, str) and s.lower() in TODO_STATUSES

Try / catch

try:
    TODO.update(items)
except ValueError as e:
    if "invalid status" in str(e):
        return f"Tool error: {e}. Allowed: pending, in_progress, completed (lowercase, underscore)."
    raise

Prevention

When it happens

Trigger: The model sends status "done", "Done" is fine (lowercased) but "finished", "in-progress" (hyphen instead of underscore), "in progress" (space), or "blocked" all fail. A missing status defaults safely to pending, so the error only comes from a present-but-wrong value.

Common situations: "done" is the single most common LLM slip. Hyphen/space variants of in_progress. Models adding custom statuses like "blocked" or "cancelled" that the harness does not model.

Related errors


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