5 Bugs That Silently Corrupted Memory Data in Uteke v0.12.0
5 Bugs That Silently Corrupted Memory Data in Uteke v0.12.0
Some bugs crash loudly. You get a stack trace, a panic, or a segfault. You know something is wrong.
Other bugs are quiet. They do not crash. They do not log errors. They silently corrupt data, and your AI agent gradually becomes wrong without anyone noticing.
Uteke v0.12.0 ships five fixes for bugs of the second kind. Each one could cause data loss or inconsistency without a single error message.
Here is what they were, how they happened, and why they matter for anyone building memory systems.
Bug 1: Config merge silently ignored 5 of 12 sections
Uteke reads its configuration from uteke.toml. The file has 12 sections: server settings, recall weights, embedding fallback rules, extraction config, and more.
The merge_from_file() function was supposed to load all of them. It loaded 7.
Five sections were silently skipped: [embed_fallback], [extraction], recall weights (salience_weight, recency_weight), and advanced fields in [server] and [limits].
Your config file said one thing. Uteke did another. No warning, no error, no log entry.
If you tuned salience_weight to 0.3 because you wanted recent memories ranked higher, Uteke ignored it and used the default 0.1. You would never know unless you read the source code.
The fix: Config merge now includes all 12 sections. We also fixed the default template that uteke init generates. It was only writing 5 of 12 sections, so new users had no reference for the settings they could not configure.
Bug 2: Vector index accumulated ghost entries
Uteke uses a usearch HNSW index for vector similarity search. When you update a document, the old embedding chunks should be removed and new ones inserted.
The doc_upsert function had the right idea: delete old chunks, then insert new ones. But the order was wrong. It deleted chunks after calling upsert_document(), which re-creates them. So the flow was:
- Insert new chunks (via upsert)
- Delete chunks associated with this document
- The delete removes both old AND new chunks from the database
- But the usearch index still has the old entries
Over time, the vector index accumulated ghost entries: embeddings for chunks that no longer existed in the database. Search results included stale data. The index grew larger than it should.
The fix: Chunk capture now happens before upsert_document() via a new get_chunk_ids_for_documents() method. Old chunks are removed cleanly. New chunks are inserted. Index and database stay in sync.
Bug 3: Deleting documents left orphaned room links
Uteke v0.11 added a room_documents junction table for linking documents to rooms. Think of it as a many-to-many relationship: a room can contain multiple documents, and a document can belong to multiple rooms.
The junction table had no foreign key constraint. When you deleted a document, the junction rows were left behind. Rooms would reference documents that no longer existed.
This is a classic SQLite pattern. Without FOREIGN KEY ... ON DELETE CASCADE, junction tables need manual cleanup. The delete_document() function did not do that cleanup.
The fix: Document deletion now runs DELETE FROM room_documents WHERE doc_slug = ?1 before deleting the document itself. Clean removal, no orphans.
Bug 4: The default config template was incomplete
When you run uteke init for the first time, Uteke generates a default uteke.toml with comments explaining each section. At least, that was the idea.
In practice, write_default_config() only wrote 5 of 12 sections. New users got a config file with no reference for embedding fallback behavior, extraction rules, or recall weight tuning. They could not configure features they did not know existed.
Not a data corruption bug, but a discoverability bug. Users were running with defaults they could not inspect or change.
The fix: write_default_config() now emits all 12 sections with inline comments. New users get a complete reference.
Bug 5: A tool name that confused everyone
The MCP tool uteke_room_document sounded like a CRUD operation. Create, read, update, or delete a document in a room.
It was not. It generated a summary document from a room's memories. The name was misleading enough that users avoided it or used it wrong.
The fix: Renamed to uteke_room_summary_document. The old name still works as a backward-compatible alias, so existing MCP clients do not break. Zero migration needed.
The pattern: silent bugs are worse than crashes
All five bugs shared a common trait: they failed silently. No error messages, no panics, no logs. The system continued operating with incorrect data.
A crash gets your attention. You investigate, find the bug, fix it. A silent corruption goes unnoticed for weeks or months. Your AI agent gives slightly wrong answers. Search results include stale data. Config files are ignored. You blame the model, the prompt, the embedding quality. You never suspect the memory layer.
This is the hardest part of building memory infrastructure. The system has to be correct even when no one is watching. Every unwrap_or(None), every skipped cleanup step, every incomplete merge is a potential silent failure.
In v0.12.0, we caught five of them. There will be more. The work is never done.
Upgrade
# Existing install: re-run the installer
curl -fsSL https://raw.githubusercontent.com/codecoradev/uteke/main/install.sh | sh
# Or build from source
git pull && cargo build --release --workspace
No manual migration needed. Schema upgrades run automatically on first launch.
Full changelog: github.com/codecoradev/uteke/blob/main/CHANGELOG.md
Release: v0.12.0