charmbracelet/crush · error
failed to get session: %w
Error message
failed to get session: %w
What it means
The session ID was present in the context but sessions.Get(ctx, sessionID) failed to load the session from the session service (SQLite-backed). The underlying error (e.g. record not found, DB failure) is wrapped with %w so the root cause is preserved for errors.Is/As.
Source
Thrown at internal/agent/tools/todos.go:48
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)
}
}
todos := make([]session.Todo, len(params.Todos))
var justCompleted []stringView on GitHub (pinned to 7944b8e522)
Solutions
- Verify the session ID exists before invoking the tool (sessions.Get or session list)
- Check the DB file exists, is writable, and migrations ran (the wrapped error tells you if it's not-found vs I/O)
- Create a fresh session if the old one was deleted
- If DB-locked, close other connections or enable busy timeout
Example fix
// before
sessions.Get(ctx, "abc123") // stale/deleted ID
// after
if _, err := sessions.Get(ctx, sessionID); err != nil {
sessionID, err = createOrResolveSession(ctx)
} Defensive patterns
Strategy: validation
Validate before calling
if _, err := sessions.Get(ctx, sessionID); err != nil {
return fmt.Errorf("session %s does not exist: %w", sessionID, err)
} Type guard
func sessionExists(ctx context.Context, svc session.Service, id string) bool {
_, err := svc.Get(ctx, id)
return err == nil
} Try / catch
var nf *session.NotFoundError
if errors.As(err, &nf) {
// recreate or re-resolve the session, then retry
} else if errors.Is(err, sqlite.ErrLocked) {
// wait/retry with backoff
} Prevention
- Check the wrapped errors.Is/As cause to distinguish not-found from DB failure
- Don't hardcode or fabricate session IDs; obtain them from the session service
- Keep the DB file writable and migrations current
When it happens
Trigger: Calling the todos tool with a session ID that does not exist in the database (deleted or stale session), the session service not being initialized/connected, or the SQLite DB being corrupted or locked.
Common situations: Resuming a session that was deleted; passing a fabricated session ID in tests; DB migration mismatch or read-only database file; concurrent access locking the SQLite store.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- error creating file history: %w
- failed to create session for non-interactive mode: %w
- failed to connect to database: %w
- failed to list sessions: %w
- failed to list messages: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/99bb7c7ce1b279fb.
Report an issue: GitHub.