JuliusBrussee/caveman · error

insert replacement memory

Error message

insert replacement memory: %w

What it means

Supersede failed while inserting the replacement memory row into the memories table. The insert runs inside the supersede transaction, so any failure (constraint violation, lock, I/O error) aborts the whole operation and no lineage change is persisted.

Solutions

  1. Check the wrapped cause (errors.Unwrap / errors.As on the driver error).
  2. Free disk space / verify write permissions on the store file.
  3. Add busy_timeout and retry on lock errors.
  4. Confirm the store file matches the current schema version.

Example fix

// before
new, err := store.Supersede(oldID, text)
if err != nil { return err }
// after
new, err := store.Supersede(oldID, text)
if err != nil {
	return fmt.Errorf("supersede %s: %w", oldID, err) // surface wrapped cause for diagnosis
}
Defensive patterns

Strategy: try-catch

Validate before calling

if err := checkDiskSpace(storeDir); err != nil { return err }

Try / catch

_, err := store.Supersede(oldID, text)
if err != nil {
	log.Printf("insert replacement failed: %v", errors.Unwrap(err))
	return err
}

Prevention

When it happens

Trigger: Calling Store.Supersede(oldID, newText) when the INSERT of the new (id, text, created_at, valid_from, supersedes) row fails: disk full, database locked, constraint violation, or corrupted schema.

Common situations: Disk quota exceeded while writing the SQLite file; concurrent writers causing SQLITE_BUSY; database file opened read-only; schema drift between library versions.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/afcab6370a605eff. Report an issue: GitHub.

Appendix: source

Thrown at mem/store.go:242

	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,
	)
	if err != nil {
		return Memory{}, fmt.Errorf("expire old memory: %w", err)
	}
	if n, _ := res.RowsAffected(); n != 1 {
		return Memory{}, fmt.Errorf("memory %s changed during supersede", oldID)
	}
	if err := tx.Commit(); err != nil {
		return Memory{}, fmt.Errorf("supersede commit: %w", err)
	}
	return Memory{
		ID:         newID,

View on GitHub (pinned to 3ee70a1026)