JuliusBrussee/caveman · warning
cannot supersede with empty text
Error message
cannot supersede with empty text
What it means
Supersede rejects empty/whitespace-only replacement text, mirroring Remember's rule: a current memory can never be replaced with a blank one. The check happens before the transaction begins, so no lineage changes occur.
Solutions
- Validate/trim the new text before calling Supersede; skip or prompt again when blank.
- To remove a memory, use the Forget-style flow instead of superseding with empty text.
- Fix the argument passing in CLI usage so the new text is provided.
- Guard interactive flows so submit with empty input is disabled.
Example fix
// before
newText := os.Getenv("NEW_TEXT") // may be empty
_, err := store.Supersede(oldID, newText)
// after
newText := strings.TrimSpace(os.Getenv("NEW_TEXT"))
if newText == "" {
return fmt.Errorf("replacement text required")
}
_, err := store.Supersede(oldID, newText) Defensive patterns
Strategy: validation
Validate before calling
// Go
func canSupersedeText(t string) bool { return strings.TrimSpace(t) != "" } Try / catch
if strings.TrimSpace(newText) == "" { return fmt.Errorf("replacement text required") }
_, err := store.Supersede(oldID, newText)
if err != nil { return err } Prevention
- Trim and non-empty-check replacement text before Supersede
- Disable empty submit in any UI feeding Supersede
- Use Forget for removal, not supersede-with-blank
- Guard shell pipelines against feeding empty stdin as text
When it happens
Trigger: Calling Store.Supersede(oldID, "") or with whitespace-only text; cmdSupersede invoked where the new-text argument was empty or a shell variable was unset.
Common situations: Scripting where the replacement text variable is unset; UI flows that allow submitting an empty edit box; piping an empty file/stdin as the new text.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- arm must be or
- artifactId is required
- cannot remember empty text
- caveman agent: file path is required
- ccr max storage bytes must be positive
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/a7142c2c113572e9.
Report an issue: GitHub.
Appendix: source
Thrown at mem/store.go:206
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")
}
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 {View on GitHub (pinned to 3ee70a1026)