charmbracelet/crush · error
invalid status %q for todo %q
Error message
invalid status %q for todo %q
What it means
Each todo item's status must be exactly "pending", "in_progress", or "completed". The model (or caller) supplied some other string, so the tool rejects the whole batch before writing. This is a strict allow-list validation, not a type check — any casing variation or synonym fails.
Source
Thrown at internal/agent/tools/todos.go:61
return fantasy.ToolResponse{}, fmt.Errorf("session ID is required for managing todos")
}
currentSession, err := sessions.Get(ctx, sessionID)
if err != nil {
return fantasy.ToolResponse{}, fmt.Errorf("failed to get session: %w", err)
}
isNew := len(currentSession.Todos) == 0
oldStatusByContent := make(map[string]session.TodoStatus)
for _, todo := range currentSession.Todos {
oldStatusByContent[todo.Content] = todo.Status
}
for _, item := range params.Todos {
switch item.Status {
case "pending", "in_progress", "completed":
default:
return fantasy.ToolResponse{}, fmt.Errorf("invalid status %q for todo %q", item.Status, item.Content)
}
}
todos := make([]session.Todo, len(params.Todos))
var justCompleted []string
var justStarted string
completedCount := 0
for i, item := range params.Todos {
todos[i] = session.Todo{
Content: item.Content,
Status: session.TodoStatus(item.Status),
ActiveForm: item.ActiveForm,
}
newStatus := session.TodoStatus(item.Status)
oldStatus, existed := oldStatusByContent[item.Content]
View on GitHub (pinned to 7944b8e522)
Solutions
- Use only the exact strings "pending", "in_progress", "completed" in the status field
- Check the tool description/JSON schema the model was given matches these values
- Pre-normalize statuses (trim, lowercase, map synonyms) before submitting the tool call
Example fix
// before
{"content":"Run tests","status":"done"}
// after
{"content":"Run tests","status":"completed"} Defensive patterns
Strategy: validation
Validate before calling
valid := map[string]bool{"pending":true, "in_progress":true, "completed":true}
for _, t := range todos {
if !valid[t.Status] {
return fmt.Errorf("status %q invalid for todo %q", t.Status, t.Content)
}
} Type guard
func isValidTodoStatus(s string) bool {
return s == "pending" || s == "in_progress" || s == "completed"
} Try / catch
if err != nil && strings.HasPrefix(err.Error(), "invalid status") {
// normalize statuses (lowercase, map synonyms) and resubmit
} Prevention
- Use only the exact lowercase snake_case statuses the tool schema documents
- Lowercase/trim model output before building the todos payload
- Add a schema-level enum constraint so invalid statuses are rejected earlier
When it happens
Trigger: The LLM emits a status like "done", "Done", "in-progress", "complete", or "In Progress" instead of the exact lowercase snake_case tokens pending/in_progress/completed in the TodosParams.Todos payload.
Common situations: LLM hallucinating non-conforming status values; a client/library version using different status vocabularies; hand-crafted tool calls in tests with wrong enum strings.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- prompt is required
- session id missing from context
- agent message id missing from context
- invalid client_id
- not a valid bedrock api key
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/f39f0be239260e41.
Report an issue: GitHub.