JuliusBrussee/caveman · error

store recovery for oversized memory

Error message

store recovery for oversized memory %s: %w

What it means

When a single recalled memory exceeds the token budget on its own, Recall falls back to a head-truncated 'cavemem-head' representation and must persist recovery metadata (the Hit with compressed text and a handle) into the store. If that storage write fails, the error is wrapped as "store recovery for oversized memory %s", naming the offending memory ID. Recall fails rather than silently returning an unrecoverable hit.

Solutions

  1. Inspect the wrapped %w error for the underlying SQLite failure.
  2. Ensure the store is open and writable (not closed, file not read-only) when calling Recall.
  3. Resolve DB lock contention from concurrent writers before recalling.
  4. Preemptively supersede or delete the oversized memory so the recovery path is not needed.

Example fix

// before
hits, err := store.Recall(ctx, q, mem.RecallOptions{TokenBudget: 100})
// after (verify store health and check wrapped error)
if err != nil {
    var id string
    if strings.Contains(err.Error(), "store recovery for oversized memory") {
        // extract ID, inspect/repair that memory, then retry
    }
    return err
}
hits, err := store.Recall(ctx, q, mem.RecallOptions{TokenBudget: 100})
Defensive patterns

Strategy: try-catch

Try / catch

hits, err := store.Recall(ctx, q, opts)
if err != nil && strings.Contains(err.Error(), "store recovery for oversized memory") {
    // DB write failed during head-truncation; check store health and retry
}

Prevention

When it happens

Trigger: Recall returns a hit whose single memory's TokensBefore exceeds the budget, and the subsequent store write of the recovery Hit record fails (DB closed, locked, corrupted, disk full).

Common situations: Recalling a very large memory with a small TokenBudget while the SQLite file is read-only or the database handle was closed; also seen when a concurrent writer holds the write lock.

Related errors


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

Appendix: source

Thrown at mem/store.go:554

// headHit builds a single recall hit for a memory whose compressed form exceeds
// the entire token budget: it truncates the compressed text to a head that fits
// and guarantees a CCR handle to the byte-exact original. When the engine passed
// the payload through without compressing (so it left no handle), the original
// is stored here — a head drops the tail, so the dropped detail must stay
// recoverable, upholding cavemem's reversible invariant.
func (s *Store) headHit(m Memory, compressedText []byte, handle string, score float64, budget int) (Hit, error) {
	head := truncateToTokens(compressedText, budget)
	if handle == "" {
		h, err := s.ccr.Put(ccr.Recovery{
			ContentType:  "text",
			Compressor:   "cavemem-head",
			TokensBefore: tokenCounter.Count([]byte(m.Text)),
			TokensAfter:  tokenCounter.Count(head),
			Original:     []byte(m.Text),
		})
		if err != nil {
			return Hit{}, fmt.Errorf("store recovery for oversized memory %s: %w", m.ID, err)
		}
		handle = h
	}
	return Hit{
		ID:             m.ID,
		Text:           string(head),
		Score:          score,
		TokensAdded:    tokenCounter.Count(head),
		Basis:          engine.BasisInferred,
		RecoveryHandle: handle,
	}, nil
}

// truncateToTokens returns the longest byte prefix of b whose inferred token
// count does not exceed budget, ending on a UTF-8 rune boundary. Recovery of the
// dropped tail is byte-exact through the hit's CCR handle.
func truncateToTokens(b []byte, budget int) []byte {
	if budget <= 0 {

View on GitHub (pinned to 3ee70a1026)