{"record":{"id":"d680dccf02568764","repo":"JuliusBrussee/caveman","slug":"count-memories-w","errorCode":null,"errorMessage":"count memories: %w","messagePattern":"count memories: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mem/store.go","lineNumber":377,"sourceCode":"\tif err := tx.Commit(); err != nil {\n\t\treturn false, fmt.Errorf(\"forget commit: %w\", err)\n\t}\n\treturn true, nil\n}\n\nfunc nullableLineageID(value sql.NullString) any {\n\tif value.Valid {\n\t\treturn value.String\n\t}\n\treturn nil\n}\n\n// Count returns the number of durable memory blocks without loading their\n// contents. Callers omit this number on error rather than presenting zero.\nfunc (s *Store) Count() (int, error) {\n\tvar count int\n\tif err := s.db.QueryRow(`SELECT COUNT(*) FROM memories WHERE valid_until IS NULL`).Scan(&count); err != nil {\n\t\treturn 0, fmt.Errorf(\"count memories: %w\", err)\n\t}\n\treturn count, nil\n}\n\n// Hit is one recall result: the recalled memory compressed for injection, with\n// the inferred token cost, the match score, and a CCR handle to recover the full\n// original when the compression dropped detail.\ntype Hit struct {\n\tID             string  `json:\"id\"`\n\tText           string  `json:\"text\"`\n\tScore          float64 `json:\"score\"`\n\tTokensAdded    int     `json:\"tokens_added\"`\n\tBasis          string  `json:\"basis\"`\n\tRecoveryHandle string  `json:\"recovery_handle,omitempty\"`\n}\n\n// RecallOptions configures Recall. A zero Threshold means DefaultThreshold; a\n// zero Limit means DefaultLimit; a zero TokenBudget means DefaultTokenBudget.","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/mem/store.go#L359-L395","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\ncount, err := store.Count()\nfmt.Printf(\"%d memories\", count) // prints 0 on error\n// after\ncount, err := store.Count()\nif err != nil {\n    return fmt.Errorf(\"memory count unavailable: %w\", err)\n}\nfmt.Printf(\"%d memories\", count)","handlingStrategy":"try-catch","validationCode":"if store == nil {\n    return errors.New(\"store not initialized before Count\")\n}","typeGuard":null,"tryCatchPattern":"count, err := store.Count()\nif err != nil {\n    return fmt.Errorf(\"memory count unavailable: %w\", err) // never treat as 0\n}","preventionTips":["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."],"tags":["database","sqlite","wrapped-error"],"backgroundTag":"database-query-failed","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}