shareAI-lab/learn-claude-code · error · ValueError
Item {i}: content required
Error message
Item {i}: content required What it means
Raised by TodoManager.update in s_full.py when a todo item's 'content' field is missing, None, or whitespace after str().strip(). The manager validates each item and reports the failing index i (0-based) so callers can locate the bad entry. No state is mutated — the whole update is rejected before self.items is replaced.
Source
Thrown at agents/s_full.py:133
return f"Error: Text not found in {path}"
fp.write_text(c.replace(old_text, new_text, 1))
return f"Edited {path}"
except Exception as e:
return f"Error: {e}"
# === SECTION: todos (s03) ===
class TodoManager:
def __init__(self):
self.items = []
def update(self, items: list) -> str:
validated, ip = [], 0
for i, item in enumerate(items):
content = str(item.get("content", "")).strip()
status = str(item.get("status", "pending")).lower()
af = str(item.get("activeForm", "")).strip()
if not content: raise ValueError(f"Item {i}: content required")
if status not in ("pending", "in_progress", "completed"):
raise ValueError(f"Item {i}: invalid status '{status}'")
if not af: raise ValueError(f"Item {i}: activeForm required")
if status == "in_progress": ip += 1
validated.append({"content": content, "status": status, "activeForm": af})
if len(validated) > 20: raise ValueError("Max 20 todos")
if ip > 1: raise ValueError("Only one in_progress allowed")
self.items = validated
return self.render()
def render(self) -> str:
if not self.items: return "No todos."
lines = []
for item in self.items:
m = {"completed": "[x]", "in_progress": "[>]", "pending": "[ ]"}.get(item["status"], "[?]")
suffix = f" <- {item['activeForm']}" if item["status"] == "in_progress" else ""
lines.append(f"{m} {item['content']}{suffix}")
done = sum(1 for t in self.items if t["status"] == "completed")View on GitHub (pinned to 985456f4ad)
Solutions
- Ensure every item includes a non-empty content string: {"content": "Implement login", ...}
- Strip and validate items client-side before calling update, dropping or fixing empty ones
- Use the reported index i to find the offending item quickly in large lists
- Default missing content to a placeholder like "(untitled step)" if your flow allows it
Example fix
// before
TODOS.update([{"content": " ", "status": "pending", "activeForm": "Working"}])
// after
TODOS.update([{"content": "Implement login", "status": "pending", "activeForm": "Working"}]) Defensive patterns
Strategy: validation
Validate before calling
def clean_todos(items):
out = []
for i, it in enumerate(items):
if not str(it.get('content', '')).strip():
it = {**it, 'content': f'(step {i})'} # or skip
out.append(it)
return out
TODOS.update(clean_todos(items)) Type guard
def is_valid_todo_item(item: object) -> bool:
return (
isinstance(item, dict)
and bool(str(item.get('content', '')).strip())
) Try / catch
try:
TODOS.update(items)
except ValueError as e:
m = re.match(r"Item (\d+): content required", str(e))
if m:
i = int(m.group(1))
items[i]['content'] = f'(step {i})'
TODOS.update(items)
else:
raise Prevention
- Require non-empty content in your tool schema (minLength: 1)
- Filter whitespace-only items before submitting
- Use the reported index to fix only the offending item
When it happens
Trigger: Calling the todo tool with [{"status": "pending", "activeForm": "x"}] (no content key), content: "" or content: " ", or content: None coerced to the string "None" only when a key exists with an empty value. Index i in the message maps to position in the submitted list.
Common situations: LLM agents emitting items with only status and activeForm; content set to an empty string when a plan step has no title yet; whitespace-only content pasted from a checklist.
Related errors
- Item {i}: invalid status '{status}'
- todos must be a list or JSON array string
- todos[{index}] requires content
- 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/404d4c7249cdc1c4.
Report an issue: GitHub.