charmbracelet/crush · error
session ID is required for managing todos
Error message
session ID is required for managing todos
What it means
The todos tool requires a session ID in its execution context to know which session's todo list to manage. GetSessionFromContext(ctx) returned an empty string, meaning the tool was invoked without the session-scoped context value that the agent runtime normally injects. This is an internal invariant error: the tool refuses to act rather than guess a session.
Source
Thrown at internal/agent/tools/todos.go:43
}
type TodosResponseMetadata struct {
IsNew bool `json:"is_new"`
Todos []session.Todo `json:"todos"`
JustCompleted []string `json:"just_completed,omitempty"`
JustStarted string `json:"just_started,omitempty"`
Completed int `json:"completed"`
Total int `json:"total"`
}
func NewTodosTool(sessions session.Service) fantasy.AgentTool {
return fantasy.NewAgentTool(
TodosToolName,
todosDescription,
func(ctx context.Context, params TodosParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
sessionID := GetSessionFromContext(ctx)
if sessionID == "" {
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)View on GitHub (pinned to 7944b8e522)
Solutions
- Ensure the tool is invoked through the normal agent/coordinator path which sets the session ID in the context
- In custom code, set the session ID in the context before calling the tool (SetSessionInContext or the equivalent context key)
- If this occurs during normal usage, report it — it indicates a bug in Crush's tool context wiring
Example fix
// before resp, err := tool.Execute(ctx, call) // ctx has no session ID // after ctx = tools.SetSessionInContext(ctx, sessionID) resp, err := tool.Execute(ctx, call)
Defensive patterns
Strategy: validation
Validate before calling
if tools.GetSessionFromContext(ctx) == "" {
return errors.New("session ID must be set in context before invoking the todos tool")
} Type guard
func hasSession(ctx context.Context) bool {
return tools.GetSessionFromContext(ctx) != ""
} Try / catch
if err != nil && strings.Contains(err.Error(), "session ID is required") {
// wire session into ctx and retry
return tool.Execute(tools.SetSessionInContext(ctx, sessionID), call)
} Prevention
- Always invoke tools via the coordinator/agent runtime, never the raw tool function
- In custom runners, replicate Crush's context wiring (session ID injection) before tool execution
- Add a test asserting tool calls fail fast without a session to catch wiring regressions
When it happens
Trigger: Calling the todos tool with a context lacking the session ID context key — e.g. invoking the fantasy.AgentTool directly in tests or custom code instead of through the coordinator, or a code path that builds the tool call context without SetSessionInContext.
Common situations: Test harnesses or scripts that invoke the tool function directly; embedding the todos tool in a custom agent runner that doesn't replicate Crush's context wiring; regressions in agent plumbing after refactoring.
Related errors
- session_id is required
- %s
- session ID is required for creating a new file
- session ID is required for accessing files outside working d
- session id missing from context
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/51877833cac6d9c4.
Report an issue: GitHub.