Sets up the foundation: an 8-crate Cargo workspace, GitHub Actions CI (fmt + clippy -D warnings + test + deny + audit), the typed identity 3-tuple in ai-memory-core, the figment-based single-read config loader, and the `ai-memory init` / `status` subcommands. Design and research that drove these choices live in docs/; CLAUDE.md holds the per-session operating rules. See design-decisions.md §14 for the cross-cutting invariants (single config read path, typed identity, atomic writes, no global singletons, …) that every milestone must respect. Verification: - cargo build --workspace clean - cargo clippy --workspace --all-targets -- -D warnings clean - cargo fmt --all -- --check clean - cargo test --workspace: 12 passed - ai-memory --version, init, status --json all working - AI_MEMORY_DATA_DIR override honoured - Second init leaves config untouched (idempotent) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
15 KiB
cognee — Issue & PR Pain-Point Synthesis
Source: GitHub
topoteretes/cognee. Captured 2026-05-21. Tracker character: ~40% feature requests (high comment counts), ~40% bugs closed fast by next release, ~20% truly hard still-open bugs at architectural seams.
Top recurring pain points (ranked)
A. LLM-adapter brittleness (highest volume, highest churn)
Every provider has had a wire-level bug in 2026.
- Anthropic adapter dropped
max_tokens, every call HTTP-422 (#2749, #2782 — two consecutive releases shipped broken Anthropic). - Ollama / LlamaCpp adapters missing
@observedecorator (#2820). - vLLM hangs because system message sent after user message (#2537).
- vLLM "custom" provider doesn't forward
LLM_ENDPOINTto LiteLLM (#2412, #2430, #2842). - LiteLLM
model_costlookup overrides user'sLLM_MAX_COMPLETION_TOKENS(#2608, fixed by #2613, #2582). - HTTP/2 stream stalls cause 60s timeouts on sequential Anthropic calls (#2607).
- Preflight LLM connection test hangs forever for non-OpenAI providers (#2752, #2123, #2380).
- Local OpenAI-compatible LM Studio / Ollama hangs on macOS (#2119, #1743, #1742).
- OPEN, unsolved: "Severe Performance Degradation Due to Thinking Tokens + Instructor Incompatibility" (#2840). User patch shows the root cause: when
response_model=str, instructor wrapsstrin a JSON/tool schema that llama.cpp doesn't honor; the LLM returns plain text, instructor fails to parse it, then tenacity retries sleep 8-128s per attempt. LiteLLM silently drops non-standard top-level kwargs likechat_template_kwargsandreasoning_effort, requiring anextra_bodyshim.
Root design choice causing this: LiteLLM + Instructor as the universal LLM gateway. Both churn fast; both silently drop kwargs they don't recognize; both have OpenAI-specific assumptions baked into the structured-output path.
B. Multi-store coordination & data integrity bugs
The "graph + vector + relational" tripartite store is the cognee architectural commitment, and it is the source of consistent, high-severity bugs.
EntityAlreadyExistsError—Entity("institution")andEntityType("institution")collide on UUID (#2510). Secondcognifyblew up.add_data_pointsparallel DB ops cause SQLitedatabase is lockeddeadlock; still reproducible in 1.0.2:multiple PipelineRunErrored ... elapsed 561s before crash(#2717, OPEN).cognifyexceeds asyncpg bind-argument limit duringupsert_edgesfor large batches (~4356 edges) (#2829, fixed via batching in #2798, #2586).add_data_pointscrashes withasyncpg.CharacterNotInRepertoireErroron null bytes (0x00) (#2612, OPEN).index_data_points: shallow copy of metadata dict — only the firstindex_fieldis embedded (#2529, OPEN).get_graph_from_model + copy_modeldrops user-defined DataPoint id (#2633, OPEN).- Edge deduplication in
retrieve_existing_edgesis non-functional (#2557). delete_datasetfails in non-"public" Postgres schema (#2291).- Shared data: deleting from one dataset wipes the same data from other datasets that share it (#2732, OPEN).
- N+1 query pattern in
/api/v1/cognify(#2532, OPEN). - KuzuAdapter writes visible in-memory but not persisted to disk on 0.5.1 (#1981).
- Per-collection distance normalization in
brute_force_triplet_searchproduces incorrect ranking (#2030, fix #2451 removed normalization, then #2720 surfaced downstream).
C. Recall quality regressions — the bug a memory server cannot afford
- #2720 (OPEN): "Graph-completion retrieval returns identical subgraph regardless of query". User-built reproducer shows direct LanceDB queries return different top-K for different queries, but cognee's
/api/v1/searchreturns ~identical answers — LanceDB IDs are not propagating into the graph projection. User attributes to fallout from #2451: downstream thresholds inbrute_force_triplet_search.pystill expect pre-#2451 [0,1] scale and silently fall back to an unfiltered graph. SearchType.CHUNKSsilently ignoresnode_namefiltering (#2815).CHUNKS/SUMMARIES/GRAPH_COMPLETIONignoresdatasets=filter whenENABLE_BACKEND_ACCESS_CONTROL=false(#2867 — maintainer answer is essentially "datasets only work with access control on").- Cognee-mcp GRAPH_COMPLETION discarded all but first dataset (#2617).
TemporalRetrieveris event-only, blocks ontology-wide temporal filtering (#2429, closed as superseded by an internal Q2 redesign — not shipped).- GRAPH_COMPLETION does not search custom DataPoint vector collections (#2495).
D. Dependency-installation hell
- macOS arm64 + Python 3.14:
kuzuwheel does not exist; quick-start fails (#2753). ModuleNotFoundError: No module named 'kuzu' in Docker since v1.0.4(#2775) — kuzu removed but image not rebuilt.fastembedremoved from the Docker image entirely because one of its transitive deps was not Apache/MIT (#2807).- LiteLLMEmbeddingEngine truncated
BAAI/bge-m3tobge-m3(#1915). embedding_dimensionsdefaulted to 3072 regardless of model — every non-3072-dim embedder broke (#2751, fix #2757).- LanceDB lance-file writer schema drift / "contained null values" RuntimeError bypassed auto-migration (#2702, #2768).
- Pydantic v1/v2 friction: upper bound conflicts with openai-agents (#2019);
.json()deprecated (#2042); generic validation issues (#1198). - Mistral client import error (#2481).
lru_cachehash invalidation bugs for Vector and Graph configs (#2357), andlru_cachewas eventually disabled outright in PR #2853 — "refactor: reduce lru cache".
E. MCP server bugs vs FastAPI
The MCP wrapper consistently lags the core API.
- MCP
cognifywith valid local file path returns success but creates no Data item (#2250, OPEN). - cognee-mcp Quick Start fails on macOS arm64 + Py 3.14 (#2753).
- cognee-mcp
cognify(data=str)silently dropped all writes after first due to hardcodeddata.txtfilename (#2747). - MCP recall in default config fails:
'NoneType' object has no attribute 'id'— wrapper does not pass user to cognee.recall (#2855). - cognee-cli
--api-urldoesn't support remember/recall/improve/forget (#2809, OPEN). - Frontend Docker build broken (#2832), Turbopack import case mismatch (#2605),
cognee-cli -uiv0.5.5 missing 3 npm deps (#2413), UI compile errors (#2709).
F. Auth / multi-tenancy regressions
- Token refresh mechanism literally not implemented; maintainer admits "we had to reimplement it for our cloud deployment. At this point, we can't allocate resources" (#2065).
- Request-scoped LLM config impossible because
get_llm_config()andget_embedding_config()use@lru_cache— singletons (#2228). - Auth disable needs two flags:
ENABLE_BACKEND_ACCESS_CONTROL=falseANDREQUIRE_AUTHENTICATION=False(#2808, fix #2836). cognee.searchignored ACL when resolving dataset by name for non-owners (#2845);cognee.addsilently created a new owner-scoped dataset when a non-owner reused a name (#2846);cognifysilently skipped data added by non-owners (#2847).- Agent display name leaked user ID (#2811).
GRAPH_DATASET_TO_DATABASE_HANDLER(user typo ofGRAPH_DATASET_DATABASE_HANDLER) was silently ignored and defaulted to kuzu (#2697). No validation on env-var names.
Design choices that caused the most issues
- Singleton config via
@lru_cache. Breaks multi-tenancy (#2228), invalidation bugs (#2357), eventually reverted (#2853). - LiteLLM + Instructor as the universal LLM/structured-output layer. Source of #2412, #2430, #2537, #2608, #2613, #2749, #2782, #2820, #2840, #2842.
- SQLite as the default relational backend with greenlet parallelism. #2717:
OperationalError: database is lockedunder parallel cognify, OPEN. Maintainer hedges: "sqlite is not there for production use-cases". - Kuzu as the default embedded graph DB. Kuzu was archived upstream (#2098), maintainers chose to replace with Ladybug (PR #2755) — a fork of Kuzu. Immediate post-replacement bugs: #2768, #2775, WAL corruption (PR #2838). The forked-DB risk has played out.
- LanceDB as the default vector store with schema migration assumed to auto-handle drift. Reality: #2702 null-values bypasses migration, #2720 retrieval pipeline drops filter on the path from vector hits to graph subgraph.
- Tripartite store with implicit sync (graph + vector + relational + optional ontology). Source of the entire integrity-bug class in section B. Orchestration is handled inside cognee, not by any transactional layer.
@lru_cache+ ContextVar mixed model for tenant isolation. #2228 explains: db config uses ContextVar pattern, but LLM and embedding configs do not.- Per-collection distance normalization in
brute_force_triplet_search(#2030) — fix #2451 removed normalization, breaking downstream threshold assumptions, surfacing as #2720. - Backend access control is the orchestration plane. When
ENABLE_BACKEND_ACCESS_CONTROL=false, all dataset-scoped retrieval silently degrades. (#2867, #2845, #2846, #2847, #2808.) - Default-LLM cost coupling:
embedding_dimensionsdefaults to 3072 (text-embedding-3-large), and litellm'smodel_costtable silently overrode usermax_completion_tokens. Two bugs from "assume OpenAI defaults" (#2751, #2608).
What the maintainers' fixes reveal
- LRU cache for configs has been quietly retreated from (#2853, #2851).
- Subprocess mode + Redis was added explicitly to escape the SQLite-greenlet trap (#2803, #2812).
- Ladybug is a Kuzu fork they own. Already shipped "fix: resolve issue with WAL file corruption for ladybug" (#2838).
- Auto-migrate LanceDB on schema drift had to be added (#2703) after lance-file writer crashed workers.
- Anthropic adapter broken for a full version cycle — #2749 then re-broken in 1.0.5 as #2782.
- Defaults flipped:
fastembedremoved from core (#2807), result-cache logging disabled by default (#2851), embedding dimensions auto-derived not defaulted (#2757), auth gated by single switch (#2836). - Feature deprecated, not fixed:
TemporalRetriever(#2429).
Open issues maintainers haven't solved
- #2717 SQLite deadlock under parallel cognify — Reproducible across versions.
- #2720 LanceDB filter not propagating to graph projection — Correctness bug in core retrieval path. No assignee.
- #2840 Thinking-token + Instructor incompatibility.
- #2532 N+1 query in
/api/v1/cognify. - #2612 null-byte crash in asyncpg.
- #2529 shallow-copy bug in
index_data_points. - #2228 request-scoped LLM/embedding config. Architectural.
- #2065 token refresh. Punted to community.
What's hard about these: they all sit at architectural seams (config plane, retrieval pipeline, async orchestration). Not one-PR fixes.
Specific dependency culprits (Rust must bet differently)
| Lib | Bug | Issue |
|---|---|---|
| litellm | drops extra_body kwargs silently; model_cost overrides user setting |
#2608, #2613, #2840 |
| instructor | wraps response_model=str in JSON schema local LLMs don't honor |
#2840 |
| tenacity | 8-128s backoff multiplies instructor's parse failures | #2840 |
| asyncpg | bind-arg limit; CharacterNotInRepertoireError on \0 |
#2829, #2612 |
| lancedb / lance-file | null-values RuntimeError bypasses migration | #2702 |
| pyarrow (under lance) | upstream of #2720 / #2702 schema drift | #2702 |
| kuzu | upstream archived, wheel gap on Py 3.14 / arm64 | #2098, #2753 |
| ladybug (fork of kuzu) | version-mapping crashes on every fresh DB; WAL corruption | #2768, PR#2838 |
| sqlite/sqlalchemy/greenlet | database-is-locked under parallel cognify | #2717 |
| anthropic SDK | max_tokens required, two consecutive releases broken |
#2749, #2782 |
| fastembed | transitive dep not Apache/MIT; removed from core | #2807 |
| pydantic | v1/v2 friction, deprecated .json(), upper bound conflicts |
#1198, #2019, #2042 |
| mistralai | client import error | #2481 |
| openai-agents | pin conflict with pydantic | #2019 |
| HF tokenizers | every word triggered HF request in chunking | #729 |
| Turbopack / npm | frontend builds repeatedly broken | #2605, #2413, #2709, #2832 |
Do-not-repeat lessons for the Rust rewrite
-
Do not put the LLM call behind a generic Python-style gateway that silently drops kwargs. Each provider gets a typed Rust client that errors on unknown fields rather than dropping them. (#2840, #2608, #2782.)
-
Don't use SQLite for write-parallel pipeline state. Use Postgres or — for embedded — a single-writer actor in front of an LMDB/Sled/SQLite-with-WAL serialized via a queue. (#2717.)
-
Don't pin a forked embedded graph DB as the default. Either commit to a battle-tested external store (Postgres + AGE, Neo4j) or build the graph primitives directly on the relational store. The Kuzu→Ladybug pivot cost real users (#2098, #2768, #2775, #2753, PR#2838).
-
Treat retrieval filter propagation as a first-class invariant with property tests. Show test cases like
assert different_queries_yield_different_subgraphs. The exact bug in #2720 is what kills a memory product. -
Configuration must be per-request from day one. No global singletons, no
lru_cacheover config. Use a request-scoped context type passed explicitly. (#2228, #2357, PR#2853.) -
Never default
embedding_dimensionsto a constant. Derive from(provider, model)at startup; refuse to start if collection-dim and model-dim mismatch. (#2751, #2757.) -
Idempotent ingestion with explicit id derivation. Node IDs must be a function of
(category, name, dataset)not justname. Property tests on cross-run determinism. (#2510, #2557, #2633.) -
Re-ingestion / pipeline-run status must be a state machine, not a flag. "PipelineRunAlreadyCompleted" prevented re-ingest of deleted files (#2097).
-
Dataset isolation must work whether or not "access control" is on. Dataset is a hard query-time filter on every retriever. (#2867, #2845, #2846, #2847.)
-
Batch every cross-store mutation. asyncpg bind-arg limit, SQLite locks, lance writer flushes — all rooted in unbounded fan-out (#2829, #2717, #2702).
-
Single transactional boundary or a documented eventual-consistency contract. Cognee's silent skips between graph/vector/relational are the deepest class of bug. Pick one.
-
Audit logs and result caches with retention from day one. Cognee's relational DB grew unbounded — 42k cached results in 9 days (#2548). They eventually disabled by default; do it before the first user hits it.
Calibration: the single most repeated lesson, weighted by both severity and recurrence: the LLM/structured-output layer (LiteLLM + Instructor) is fragile, and the multi-store sync (graph + vector + relational) is the deepest source of correctness bugs. A Rust rewrite that gets either of those wrong inherits cognee's tracker.