{"record":{"id":"97a12050dc7e4aab","repo":"JuliusBrussee/caveman","slug":"read-memory-s-w","errorCode":null,"errorMessage":"read memory %s: %w","messagePattern":"read memory (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mem/store.go","lineNumber":649,"sourceCode":"\t\t&supersededBy,\n\t); err != nil {\n\t\treturn err\n\t}\n\tif validUntil.Valid {\n\t\tmemory.ValidUntil = &validUntil.String\n\t}\n\tmemory.Supersedes = supersedes.String\n\tmemory.SupersededBy = supersededBy.String\n\treturn nil\n}\n\nfunc (s *Store) memoryByID(id string) (Memory, error) {\n\tvar memory Memory\n\tif err := scanMemory(s.db.QueryRow(memorySelect+` WHERE id = ?`, id), &memory); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn Memory{}, fmt.Errorf(\"memory %s not found\", id)\n\t\t}\n\t\treturn Memory{}, fmt.Errorf(\"read memory %s: %w\", id, err)\n\t}\n\treturn memory, nil\n}\n\n// migrateMemorySchema upgrades pre-supersession stores in place. SQLite cannot\n// add several columns in one statement, so each missing column is added\n// independently and legacy created_at becomes valid_from.\nfunc migrateMemorySchema(db *sql.DB) error {\n\trows, err := db.Query(`PRAGMA table_info(memories)`)\n\tif err != nil {\n\t\treturn err\n\t}\n\tcolumns := map[string]bool{}\n\tfor rows.Next() {\n\t\tvar cid int\n\t\tvar name, kind string\n\t\tvar notNull, primaryKey int\n\t\tvar defaultValue any","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/mem/store.go#L631-L667","documentation":"In memoryByID, any scan/query error other than sql.ErrNoRows is wrapped as \"read memory <id>: %w\". This distinguishes real read failures (corruption, lock, schema drift) from the benign not-found case, preserving the underlying cause for diagnosis.","triggerScenarios":"Store.History(id) with an existing ID but a failing underlying read: DB locked, corrupted, disk error, or a scanMemory type/conversion failure caused by schema drift (e.g. pre-migration rows with unexpected NULLs).","commonSituations":"Opening a database written by an older cavemem version before migrateMemorySchema completed, or reading while another process holds a long write transaction.","solutions":["Read the wrapped %w error to find the actual SQLite/scan failure.","Reopen the store so migrations run if the schema is from an older version.","Wait for or eliminate the competing writer holding the DB lock.","If corruption is indicated, restore the database from backup."],"exampleFix":"// before\nm, err := store.History(id)\n// after\nif err != nil {\n    if !strings.Contains(err.Error(), \"not found\") {\n        log.Printf(\"read memory %s failed: %v\", id, err) // inspect wrapped cause\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"m, err := store.History(id)\nif err != nil {\n    if strings.Contains(err.Error(), \"read memory\") {\n        log.Printf(\"underlying cause: %v\", errors.Unwrap(err))\n    }\n    return err\n}","preventionTips":["Inspect errors.Unwrap(err) for the true SQLite failure.","Let store-open migrations complete before reads.","Resolve writer locks (check for stale WAL files or hung processes)."],"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"}