JuliusBrussee/caveman · error
supersede begin
Error message
supersede begin: %w
What it means
Supersede wraps failures from s.db.Begin() with this error. The atomic old→new replacement needs a transaction; if one cannot be started (db locked, closed, corrupted), Supersede aborts before any reads or writes, leaving lineage untouched.
Solutions
- Check for other connections/processes holding write transactions on the same db; serialize access or retry with backoff.
- Ensure the Store is not used after Close(); keep the Store alive for the call's lifetime.
- Verify the db file and its directory are writable (journal/WAL files).
- Inspect the wrapped cause to distinguish lock vs closed vs corrupt db.
Example fix
// before
_, err := store.Supersede(oldID, newText)
if err != nil { panic(err) }
// after
_, err := store.Supersede(oldID, newText)
if err != nil && strings.Contains(err.Error(), "database is locked") {
time.Sleep(250 * time.Millisecond)
_, err = store.Supersede(oldID, newText) // bounded retry
} Defensive patterns
Strategy: retry
Validate before calling
// Go: ensure the store is still open before use
if store == nil { return fmt.Errorf("store closed") } Try / catch
_, err := store.Supersede(oldID, newText)
if err != nil && strings.Contains(err.Error(), "supersede begin") {
time.Sleep(backoff)
_, err = store.Supersede(oldID, newText) // bounded retry on lock
} Prevention
- Avoid holding long transactions on the memory db from other connections
- Never use the Store after Close()
- Keep db and journal files on a writable mount
- Serialize supersede operations across processes
When it happens
Trigger: Calling Supersede when the SQLite db is heavily contended (long-held write lock by another connection), the store was already closed via Store.Close(), or the db file is unreadable/corrupt.
Common situations: Another process in a long transaction on the same memory db; calling Supersede after defer Store.Close() ran; read-only filesystem mounts preventing the write-journal setup.
Related errors
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/ff7b2748813654ce.
Report an issue: GitHub.
Appendix: source
Thrown at mem/store.go:213
return stored, nil
}
// Supersede atomically replaces one current memory with a new current version.
// The old row remains durable for history and byte-exact audit, but normal recall
// excludes it. An already-expired or unknown id fails closed.
func (s *Store) Supersede(oldID, newText string) (Memory, error) {
if strings.TrimSpace(oldID) == "" {
return Memory{}, fmt.Errorf("supersede requires old memory id")
}
if strings.TrimSpace(newText) == "" {
return Memory{}, fmt.Errorf("cannot supersede with empty text")
}
if err := validateMemorySize(newText); err != nil {
return Memory{}, err
}
tx, err := s.db.Begin()
if err != nil {
return Memory{}, fmt.Errorf("supersede begin: %w", err)
}
defer tx.Rollback()
var old Memory
if err := scanMemory(tx.QueryRow(memorySelect+` WHERE id = ? AND valid_until IS NULL`, oldID), &old); err != nil {
if err == sql.ErrNoRows {
return Memory{}, fmt.Errorf("memory %s is not current", oldID)
}
return Memory{}, fmt.Errorf("read current memory: %w", err)
}
if old.Text == newText {
return Memory{}, fmt.Errorf("replacement text is identical to current memory")
}
newID := memID(newText)
var exists int
if err := tx.QueryRow(`SELECT COUNT(*) FROM memories WHERE id = ?`, newID).Scan(&exists); err != nil {
return Memory{}, fmt.Errorf("check replacement memory: %w", err)
}View on GitHub (pinned to 3ee70a1026)