JuliusBrussee/caveman · error

store memory

Error message

store memory: %w

What it means

Remember() wraps failures from the INSERT OR IGNORE INTO memories statement (run through ccr.RetryOnBusy) with this error. It means the memory text could not be durably written to the SQLite store, so Remember returns no Memory. Because the raw text write is the first durable step, this failure means nothing was remembered.

Solutions

  1. Read the wrapped cause: 'database is locked' → reduce concurrent writers or increase retry tolerance; 'readonly database' → fix permissions/mount.
  2. Check free disk space and quotas.
  3. Verify the store was created via mem.Open so the schema exists.
  4. Back up and recreate the db if it is corrupt.
  5. Retry Remember after transient lock contention clears.

Example fix

// before
m, err := store.Remember(text)
if err != nil { panic(err) }
// after
m, err := store.Remember(text)
if err != nil {
    if strings.Contains(err.Error(), "database is locked") {
        time.Sleep(100 * time.Millisecond)
        m, err = store.Remember(text) // bounded retry
    }
    if err != nil { return fmt.Errorf("remember %q: %w", text, err) }
}
Defensive patterns

Strategy: retry

Validate before calling

// Go: confirm store is open and db file writable before Remember
if store == nil { return fmt.Errorf("store not open") }

Try / catch

m, err := store.Remember(text)
if err != nil {
    if strings.Contains(err.Error(), "database is locked") {
        time.Sleep(backoff); m, err = store.Remember(text) // bounded retries
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Calling Remember when the SQLite db is locked beyond the busy-retry budget, the disk is full, the db file is corrupt/read-only, or the schema is missing (e.g. store constructed without Open).

Common situations: Concurrent writers to the same memory db (SQLITE_BUSY), running from a read-only container filesystem, disk quota exhaustion, or a corrupted db after an unclean shutdown.

Related errors


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

Appendix: source

Thrown at mem/store.go:185

	if strings.TrimSpace(text) == "" {
		return Memory{}, fmt.Errorf("cannot remember empty text")
	}
	if err := validateMemorySize(text); err != nil {
		return Memory{}, err
	}
	id := memID(text)
	created := time.Now().UTC().Format(time.RFC3339Nano)
	// INSERT OR IGNORE keeps the original created_at on a repeat remember.
	// RetryOnBusy: under a fan-out of cavemem processes, a queued writer can
	// outlast a single busy_timeout (Windows file locking is the slowest case).
	if err := ccr.RetryOnBusy(func() error {
		_, e := s.db.Exec(
			`INSERT OR IGNORE INTO memories (id, text, created_at, valid_from) VALUES (?, ?, ?, ?)`,
			id, text, created, created,
		)
		return e
	}); err != nil {
		return Memory{}, fmt.Errorf("store memory: %w", err)
	}
	var stored Memory
	row := s.db.QueryRow(memorySelect+` WHERE id = ?`, id)
	if err := scanMemory(row, &stored); err != nil {
		return Memory{}, fmt.Errorf("read back memory: %w", err)
	}
	if stored.ValidUntil != nil {
		return Memory{}, fmt.Errorf("memory %s exists only as an expired historical version and cannot be re-remembered as current", stored.ID)
	}
	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")

View on GitHub (pinned to 3ee70a1026)