Skip to content

knowledge: browser empty + 'unvectorized on every reload' — facts (Redis) wiped 43→0 while 22 ChromaDB vectors remain; facts not durably persisted; vectorized status read from Redis flag not the vector store #12733

Description

@mrveiss

Summary

The Knowledge Browser (/knowledge/browser) renders empty, and categories show as unvectorized on every reload. Root cause: the knowledge base's two stores are out of sync and its user data is not durably persisted. Facts (stored in Redis) have disappeared — facts/by_category went from 43 facts / ~3 MB earlier this session to 0 now — while ChromaDB still holds the vectors. The KB is internally inconsistent, and the browser (which reads facts) shows nothing.

Evidence

GET /api/knowledge_base/facts/by_category  ->  {"categories":{}, "total_facts":0}   (was 43 facts, ~3 MB)
GET /api/knowledge_base/detailed_stats     ->  total_facts: 0 | total_documents: 22 | total_vectors: 22
                                               chromadb_collection: autobot_memory | redis_db: 1
GET /api/knowledge_base/categories         ->  categories exist (autobot_memory, …)   ← structure present, data gone

So: 0 facts, but 22 documents / 22 vectors in ChromaDB. Categories still exist, so the browser draws the tree structure with no items underneath → it looks empty.

Why the facts vanished

  • Fact storage sets no TTL — knowledge/facts.py::_store_fact_in_redis uses hset with no expire/setex, so they don't age out.
  • Redis has been up 4 days (uptime_in_days:4), with RDB persistence on (save 3600 1 …, dump.rdb current) and maxmemory-policy noeviction — so they were not lost to a Redis restart or eviction.
  • No KB cleanup job ran to remove them (see the celery note below — those tasks aren't even registered).

That leaves: the facts were wiped by something on the app side — most likely a backend re-init (there were 10 backend restarts today) that cleared/re-seeded the facts store without restoring, or a delete operation. Either way, user knowledge data was lost, which for a "your data, your AI" product is the most serious failure class.

Durability concern (design)

Knowledge facts are stored in Redis (redis_db: 1), while their vectors are in ChromaDB (disk). Redis with RDB snapshots is not a durable system of record for user data — a snapshot gap, a re-init, or a flush loses facts, exactly as observed, while the ChromaDB vectors survive (hence the 0-facts/22-vectors split). The system of record for user knowledge should be durable (Postgres/disk), with Redis as a cache — not the sole home of the data.

"Unvectorized on every reload"

Vectorization status is tracked as fact metadata in Redis (vectorized_at on the fact hash, knowledge/facts.py:732), not derived from the actual vector store. So:

  • When facts are gone but vectors remain, there is nothing to mark "vectorized," and the UI reports everything unvectorized.
  • More generally, because status is stored beside the fact instead of computed from ChromaDB membership, any Redis/ChromaDB drift makes the vectorized indicator wrong, and it re-reads as unvectorized on each reload.

Fix: derive vectorized state from the vector store (is there a vector for this id in ChromaDB?), or reconcile the two stores on read, rather than trusting a Redis-side flag that can drift.

Related — KB cleanup tasks not registered (#12318 still live)

The backend now logs (good — the startup assertion recommended in #12318 landed):

CRITICAL Beat-scheduled tasks are NOT registered and will be dropped:
  [... 'tasks.cleanup_expired_kb_entries', 'tasks.cleanup_orphan_documents', ...]

So KB cleanup/expiry tasks are scheduled but never run (they didn't cause this loss, but it means orphan/expiry maintenance is dead — #12318). Reinforces that the KB's store lifecycle is unmanaged.

Recommendation

  1. Make user knowledge durable — persist facts to a durable store (Postgres/disk) as the system of record; use Redis as a cache rebuilt from it. No user data should live only in Redis.
  2. Reconcile the two stores — on startup and on read, ensure Redis facts and ChromaDB vectors agree; expose a repair that rebuilds one from the other (there are 22 orphaned vectors right now with no facts).
  3. Derive vectorized status from the vector store, not a Redis-side flag, so the browser stops showing "unvectorized" for data that is vectorized.
  4. Find and fix the wipe — determine why facts cleared across the backend restarts (re-init clearing db1? a bulk-delete path?) so it can't silently drop user knowledge again.
  5. Fix celery: autodiscover_tasks registers nothing (no tasks/tasks.py) — 4 beat-scheduled jobs silently never run, incl. credential reconcile, snapshot cleanup, LLC sprint auto-close #12318 so KB lifecycle tasks actually run.

Impact

Critical — the core Knowledge feature shows empty, user-entered facts were lost, and the vectorized indicator is unreliable. This is the flagship "your data" surface; silent loss of it is the worst outcome for the product's positioning.

Environment

install.sh deploy at /opt/autobot; Redis (facts, db1) + ChromaDB (vectors, autobot_memory). 10 backend restarts today. Related: #12312 (embedding drops — same dual-store drift), #12364/#12365 (dual-mechanism pattern), #12318 (celery tasks unregistered).

Activity

  1. mrveiss commented on Jul 29, 2026

    @mrveiss
    OwnerAuthor

    Possible contributor found while tracing #12969, worth checking before diagnosing this independently.

    services.knowledge.doc_indexer reports on every boot of the live install:

    2026-07-29 11:50:37 - WARNING - No documentation files discovered in /opt/autobot/autobot-backend
    

    It sets self._root_dir = PATH.PROJECT_ROOT (doc_indexer.py:507), which resolves to the backend directory on a deployed host — while the documentation tree is a sibling at /opt/autobot/docs. In a checkout PROJECT_ROOT is the repo root and docs/ sits under it, so indexing works there and silently indexes nothing in deployment.

    If any part of "knowledge browser empty" depends on indexed documentation, this would produce exactly that with no error surfaced — the indexer logs a warning and continues.

    Root cause and the other three affected consumers are on #12969; it is a PROJECT_ROOT semantics problem rather than anything specific to the knowledge base.

  2. mrveiss commented on Sep 4, 2026

    @mrveiss
    OwnerAuthor

    The general rule behind this is now tracked as #15663 (sub-issue of #13916): no datum in AutoBot declares its system of record, so the Redis-homed facts here are one instance of a repo-wide gap rather than a KB-local bug. This issue stays the concrete fix — #15663 owns the rule and the guard test that stops the next one.

  3. mrveiss commented on Sep 5, 2026

    @mrveiss
    OwnerAuthor

    Two of the five recommendations here are now merged, via #15663 / PR #15668 (324fde1a). Leaving this open — the other three are untouched.

    Done — recommendation 1 (durable system of record). Facts now live in a knowledge_facts Postgres table (autobot-backend/models/knowledge_fact.py, migration 20260825_085). The durable row is written before the Redis projection (knowledge/facts.py:771-772), and the write is allowed to fail the call: a fact reported stored has been recorded. _read_fact_for_write settles existence from the row when the hash is gone — the exact state that made every fact here read as "not found" while the knowledge was still recoverable — and rebuild_fact_projections() reconstructs every fact:* key, dedup index, session link and ownership set from the rows.

    Facts that predate the change are adopted by adopt_legacy_facts(), wired at knowledge_factory.py:101 and :209. That matters for this issue specifically: the migration creates the table empty (the migration gate has no Redis connection), so without the adoption the 22-orphaned-vector deployment would still have had Redis-only facts. It runs on KB construction, so the first backend restart after deploying is when existing facts actually get their rows.

    Done — recommendation 3 (derive vectorized status). knowledge/vector_membership.py::vectorized_ids asks ChromaDB directly and returns None, not an empty set, when the store is unreachable — so "don't know" cannot render as "nothing is vectorized", which is what produced the "unvectorized on every reload" symptom. Both readers go through it now: the status endpoint (api/knowledge_vectorization.py:47) and — the one that actually kept the damage alive — the reconciler (background_vectorization.py:25, deciding at :119). It previously skipped any fact stamped vectorization_status=completed, so a vector lost underneath a surviving stamp was never rebuilt, in the one component whose job is repairing that drift. repo_tests/store_authority_test.py::test_vectorized_status_is_derived_not_stored fails if either deciding function reads the flag again.

    Still open here:

    1. Reconcile the two stores / expose a repair. rebuild_fact_projections() covers Postgres → Redis and adopt_legacy_facts() covers Redis → Postgres, but neither is reachable from an endpoint, and the 22 orphaned ChromaDB vectors with no fact behind them are not addressed by either.
    2. Find the wipe. Why the facts cleared across the backend restarts is still unknown. The durable store means a repeat is survivable, not that it cannot happen — and if something is issuing a bulk delete, it will now delete the rows too.
    3. celery: autodiscover_tasks registers nothing (no tasks/tasks.py) — 4 beat-scheduled jobs silently never run, incl. credential reconcile, snapshot cleanup, LLC sprint auto-close #12318 (KB lifecycle tasks unregistered) is unchanged.

    The general rule this instance motivated is #15663, now closed: every persisted concept declares its system of record, and repo_tests/store_authority_test.py fails when a new one appears without.

  4. modified the milestones: Backlog, v0.13.0 on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions