{"record":{"id":"a7142c2c113572e9","repo":"JuliusBrussee/caveman","slug":"cannot-supersede-with-empty-text","errorCode":null,"errorMessage":"cannot supersede with empty text","messagePattern":"cannot supersede with empty text","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mem/store.go","lineNumber":206,"sourceCode":"\trow := s.db.QueryRow(memorySelect+` WHERE id = ?`, id)\n\tif err := scanMemory(row, &stored); err != nil {\n\t\treturn Memory{}, fmt.Errorf(\"read back memory: %w\", err)\n\t}\n\tif stored.ValidUntil != nil {\n\t\treturn Memory{}, fmt.Errorf(\"memory %s exists only as an expired historical version and cannot be re-remembered as current\", stored.ID)\n\t}\n\treturn stored, nil\n}\n\n// Supersede atomically replaces one current memory with a new current version.\n// The old row remains durable for history and byte-exact audit, but normal recall\n// excludes it. An already-expired or unknown id fails closed.\nfunc (s *Store) Supersede(oldID, newText string) (Memory, error) {\n\tif strings.TrimSpace(oldID) == \"\" {\n\t\treturn Memory{}, fmt.Errorf(\"supersede requires old memory id\")\n\t}\n\tif strings.TrimSpace(newText) == \"\" {\n\t\treturn Memory{}, fmt.Errorf(\"cannot supersede with empty text\")\n\t}\n\tif err := validateMemorySize(newText); err != nil {\n\t\treturn Memory{}, err\n\t}\n\ttx, err := s.db.Begin()\n\tif err != nil {\n\t\treturn Memory{}, fmt.Errorf(\"supersede begin: %w\", err)\n\t}\n\tdefer tx.Rollback()\n\n\tvar old Memory\n\tif err := scanMemory(tx.QueryRow(memorySelect+` WHERE id = ? AND valid_until IS NULL`, oldID), &old); err != nil {\n\t\tif err == sql.ErrNoRows {\n\t\t\treturn Memory{}, fmt.Errorf(\"memory %s is not current\", oldID)\n\t\t}\n\t\treturn Memory{}, fmt.Errorf(\"read current memory: %w\", err)\n\t}\n\tif old.Text == newText {","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/mem/store.go#L188-L224","documentation":"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.","triggerScenarios":"Calling Store.Supersede(oldID, \"\") or with whitespace-only text; cmdSupersede invoked where the new-text argument was empty or a shell variable was unset.","commonSituations":"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.","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."],"exampleFix":"// before\nnewText := os.Getenv(\"NEW_TEXT\") // may be empty\n_, err := store.Supersede(oldID, newText)\n// after\nnewText := strings.TrimSpace(os.Getenv(\"NEW_TEXT\"))\nif newText == \"\" {\n    return fmt.Errorf(\"replacement text required\")\n}\n_, err := store.Supersede(oldID, newText)","handlingStrategy":"validation","validationCode":"// Go\nfunc canSupersedeText(t string) bool { return strings.TrimSpace(t) != \"\" }","typeGuard":null,"tryCatchPattern":"if strings.TrimSpace(newText) == \"\" { return fmt.Errorf(\"replacement text required\") }\n_, err := store.Supersede(oldID, newText)\nif err != nil { return err }","preventionTips":["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"],"tags":["validation","argument","empty-value"],"backgroundTag":"empty-required-field","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}