JuliusBrussee/caveman · error
count memories
Error message
count memories: %w
What it means
Store.Count wraps any error from the SQLite `SELECT COUNT(*) FROM memories WHERE valid_until IS NULL` query with the "count memories" prefix. It exists so callers can distinguish a durable-memory count failure from a legitimate zero and avoid presenting 0 on error. The wrapper preserves the underlying database error via %w.
Solutions
- Check that the store was opened successfully and Close() has not been called before Count().
- Inspect the wrapped %w error to identify the SQLite failure (corruption, disk full, schema missing).
- Reopen or recreate the store so migrations run and the memories table exists.
- Retry after resolving contention if the DB was locked by another process.
Example fix
// before
count, err := store.Count()
fmt.Printf("%d memories", count) // prints 0 on error
// after
count, err := store.Count()
if err != nil {
return fmt.Errorf("memory count unavailable: %w", err)
}
fmt.Printf("%d memories", count) Defensive patterns
Strategy: try-catch
Validate before calling
if store == nil {
return errors.New("store not initialized before Count")
} Try / catch
count, err := store.Count()
if err != nil {
return fmt.Errorf("memory count unavailable: %w", err) // never treat as 0
} Prevention
- Never treat a Count error result as zero; branch on err explicitly.
- Open the store once and keep it open for the session's lifetime.
- Watch the wrapped error for 'no such table' — signals skipped migrations.
When it happens
Trigger: Calling Store.Count() when the underlying SQLite database is corrupted, locked by another writer, closed, or when the memories table/schema is missing (e.g. store never migrated, file deleted mid-session).
Common situations: Tests like TestCount and TestForgetRepairsHistoryWithoutResurrectingExpiredMemory call Count after store mutations; in production this appears when the DB file is on a full disk, the store was closed before Count, or a schema migration failed leaving no memories table.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/d680dccf02568764.
Report an issue: GitHub.
Appendix: source
Thrown at mem/store.go:377
if err := tx.Commit(); err != nil {
return false, fmt.Errorf("forget commit: %w", err)
}
return true, nil
}
func nullableLineageID(value sql.NullString) any {
if value.Valid {
return value.String
}
return nil
}
// Count returns the number of durable memory blocks without loading their
// contents. Callers omit this number on error rather than presenting zero.
func (s *Store) Count() (int, error) {
var count int
if err := s.db.QueryRow(`SELECT COUNT(*) FROM memories WHERE valid_until IS NULL`).Scan(&count); err != nil {
return 0, fmt.Errorf("count memories: %w", err)
}
return count, nil
}
// Hit is one recall result: the recalled memory compressed for injection, with
// the inferred token cost, the match score, and a CCR handle to recover the full
// original when the compression dropped detail.
type Hit struct {
ID string `json:"id"`
Text string `json:"text"`
Score float64 `json:"score"`
TokensAdded int `json:"tokens_added"`
Basis string `json:"basis"`
RecoveryHandle string `json:"recovery_handle,omitempty"`
}
// RecallOptions configures Recall. A zero Threshold means DefaultThreshold; a
// zero Limit means DefaultLimit; a zero TokenBudget means DefaultTokenBudget.View on GitHub (pinned to 3ee70a1026)