JuliusBrussee/caveman · error
check replacement memory
Error message
check replacement memory: %w
What it means
Supersede failed while checking whether a memory with the replacement's ID already exists (SELECT COUNT(*) FROM memories WHERE id = ?). Any scan/query error other than a clean result is wrapped here and the transaction is abandoned so no partial lineage is written.
Solutions
- Inspect the wrapped cause with errors.Unwrap to identify lock vs I/O vs schema failure.
- Set a busy_timeout pragma/DSN option and retry on SQLITE_BUSY.
- Check disk space and file permissions on the store path.
- Recreate or migrate the store file if the schema is outdated.
Example fix
// before
mem, err := store.Supersede(oldID, text)
if err != nil { return err }
// after
mem, err := store.Supersede(oldID, text)
if err != nil {
var serr *sqlite.Error
if errors.As(err, &serr) && serr.Code() == sqlite.RESULT_BUSY {
time.Sleep(100 * time.Millisecond)
mem, err = store.Supersede(oldID, text)
}
if err != nil { return err }
} Defensive patterns
Strategy: retry
Validate before calling
if _, err := os.Stat(storePath); err != nil { return err } Try / catch
for i := 0; i < 3; i++ {
if _, err := store.Supersede(oldID, text); err == nil || !strings.Contains(err.Error(), "locked") {
return err
}
time.Sleep(100 * time.Millisecond << i)
} Prevention
- Enable busy_timeout to absorb SQLITE_BUSY
- Limit concurrent writers to the store
- Verify store file permissions and schema version before writing
When it happens
Trigger: Calling Store.Supersede(oldID, newText) when the COUNT query on the memories table fails: locked/corrupted database, disk I/O error, or schema mismatch.
Common situations: SQLite busy from concurrent writers; read-only or damaged database file; store opened with a different schema version than the one that created it.
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/9c179b16e3c277a3.
Report an issue: GitHub.
Appendix: source
Thrown at mem/store.go:230
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)
}
if exists > 0 {
return Memory{}, fmt.Errorf("replacement memory %s already exists", newID)
}
now := time.Now().UTC().Format(time.RFC3339Nano)
if _, err := tx.Exec(
`INSERT INTO memories
(id, text, created_at, valid_from, supersedes)
VALUES (?, ?, ?, ?, ?)`,
newID, newText, now, now, oldID,
); err != nil {
return Memory{}, fmt.Errorf("insert replacement memory: %w", err)
}
res, err := tx.Exec(
`UPDATE memories
SET valid_until = ?, superseded_by = ?
WHERE id = ? AND valid_until IS NULL`,
now, newID, oldID,View on GitHub (pinned to 3ee70a1026)