shareAI-lab/learn-claude-code · error · ValueError
todos[{index}] requires content
Error message
todos[{index}] requires content What it means
Raised by TodoManager.update in s05_todo_write/code.py when a todo object's content is missing, empty, or whitespace-only after strip(). The index in the message refers to the element's position in the submitted list, letting callers pinpoint the offender. The batch is rejected atomically, so previous todos survive.
Source
Thrown at s05_todo_write/code.py:138
todos = ast.literal_eval(todos)
except (SyntaxError, ValueError) as e:
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:View on GitHub (pinned to 985456f4ad)
Solutions
- Give every item a descriptive non-empty content string
- Filter empty items before the call: [t for t in todos if str(t.get('content', '')).strip()]
- Use the reported index to fix just the offending element and resubmit the whole list
Example fix
// before
TODOS.update([{"status": "pending"}])
// after
TODOS.update([{"content": "write tests", "status": "pending"}]) Defensive patterns
Strategy: validation
Validate before calling
todos = [t for t in todos if str(t.get('content', '')).strip()]
assert all(str(t.get('content', '')).strip() for t in todos)
TODOS.update(todos) Type guard
def has_content(todo: object) -> bool:
return isinstance(todo, dict) and bool(str(todo.get('content', '')).strip()) Try / catch
try:
TODOS.update(todos)
except ValueError as e:
m = re.search(r"todos\[(\d+)\] requires content", str(e))
if m:
i = int(m.group(1))
todos[i]['content'] = f"(step {i})"
TODOS.update(todos)
else:
raise Prevention
- Require content (minLength: 1) in the tool schema
- Drop empty items before submission
- Use the index in the message to locate the offender
When it happens
Trigger: An element like {"status": "pending"} with no content key, {"content": ""}, or {"content": " "}. Note the module does not require activeForm (unlike s_full), only content and a valid status.
Common situations: LLM emitting a status-only item as a separator or header; whitespace content from trimmed plan text; optional-field confusion where the caller assumes content is optional.
Related errors
- Item {i}: content required
- Item {i}: invalid status '{status}'
- todos must be a list or JSON array string
- Max 20 todos allowed
- Item {item_id}: text required
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/a22db88d031196fb.
Report an issue: GitHub.