11 KiB
basic-memory - Issue & PR Pain-Point Synthesis
Source: GitHub
basicmachines-co/basic-memory. Captured 2026-05-21. Tracker is unusually high-signal — small team, deep technical replies. Dominant themes: sync correctness, multi-project routing, embedding install hell.
Top recurring pain points (ranked)
1. Sync correctness & file-watcher reliability - the single biggest theme
- #580 - watch service can go stale while process stays alive (no heartbeat, no liveness signal,
awatch()blocks forever on macOS FSEvents buffer overflow). Closed with partial fix. - #758 - watch service ignored the
--projectconstraint, N concurrent MCP processes produced N overlapping watchers and races. The fix found three independent bugs while fixing one. - #798 - watch service silently dropped events under hidden-directory parents (e.g., projects under
~/.claude/...) because gitignore-style globs treated every component starting with.as hidden. - #765 - "Stale FTS index entries persist after
reset --reindex": fixed by detecting zombie MCP processes holding the old SQLite inode afterunlink. > "That process keeps its connection to the oldmemory.dbinode (now unlinked but not freed)... newly-spawned MCP processes attach to the new file. But any tool call that gets routed to a zombie MCP process queries the old inode." Nowbm resetrefuses to run while MCP processes are alive (PR #776). - #763 -
write_notereturns before semantic indexing completes. Maintainer defends this as intentional architecture but admits the CLI path loses embeddings on process exit. - #839 (OPEN) - CLI
write-noteprintsCancelledErrortraceback because_log_task_failuredoesn't handle task cancellation on process exit. Same root cause as #763. - #578 - new entities silently skip embedding generation after a single sqlite-vec load failure. Background task errors fire-and-forget.
- #634 - schema-validate uses stale
entity_metadatawhen files edited externally. - #481 -
alembic/env.pywas unconditionally settingBASIC_MEMORY_ENV="test"at module import time, which silently disabled the watch service in production.
2. Multi-project / multi-workspace routing - the v0.20 trauma cluster
A wave of nearly identical bugs in April–May 2026: #782, #783, #788, #793, #799, #800, #802, #803, #804, #805, #810, #820, #834. Every one of them is the same shape: an MCP tool ignores or mis-resolves a project/workspace identifier. Maintainer on #783: > "Permalinks are unique per project but project is not unique per workspace... any tool that holds only a permalink string cannot distinguish between them." Architecture catching up to ambition: permalinks were designed as a 2-tuple (project, path) and later had to grow a third dimension (workspace) under load.
3. SQLite-vec / embedding provider install hell
- #735 / #767 - duplicates: "no such module: vec0" on Windows in worker connections. Fixed by ensuring
_ensure_sqlite_vec_loadedis called on every session that touches vec0. - #829 / #658 - still-open variants.
- #741 / #681 - FastEmbed cache defaulted to
/tmp/fastembed_cache, wiped in sandboxed runtimes like Codex CLI, causingONNXRuntimeError: NO_SUCHFILEon every subsequent semantic search. - #830 (OPEN) -
docker-compose-postgres.ymlships plainpostgres:17instead ofpgvector/pgvector:pg17; semantic search silently fails. - #831 (OPEN) -
IndexError: pop from an empty dequeduring async engine dispose on Postgres/asyncpg.
4. Parser fragility (markdown-as-source-of-truth)
- #738 - Parser captured Obsidian callout syntax (
> [!note]) as observation categories. - #721 -
edit_notefails on notes with long text around inline wikilinks becauserelation_typeexceedsMaxLen. - #528 - Cloud sync prepended duplicate frontmatter to files that already had YAML.
- #408 - YAML frontmatter parsing fails with unquoted colons in title.
- #256 - "Editing notes causes them to disappear from index" - the search index DELETE was missing a
project_idfilter, so an edit in one project nuked search rows in every project with the same permalink.
5. Search quality / pagination
- #693 -
read_notepagination params were ignored at the API endpoint. - #354 -
tag:tagnamesyntax silently treated as literal text. - #686 (OPEN) - user hitting MCP response size limits at 57 pages.
- #666, #618, #603 (all OPEN) - reranking, time-decay, length normalization. Search ranking is admitted-weak by maintainers.
6. Backup / undo / git
- #124 (OPEN since June 2025) - git-based undo. Maintainer wrote design spec but issue remains open: > "there hasn't been an obvious way to handle when to commit. Commit on every change? Commit every so often, how often? Push to a remote? How to handle conflicts?"
- #59 - use
git diffto prevent knowledge corruption/deletion. OPEN since March 2025.
7. Manual capture friction - present but indirect
Nobody filed "I'm tired of telling the agent to remember." But:
- #297 - Cursor + basic-memory produce sensationalized progress logs (
BREAKTHROUGH - Live Data Packets Detected!) that pollute search results. Maintainer closes as not basic-memory's fault, it's the LLM's: > "It is just the tools. Your LLM is in charge of how to use it." That's the philosophical commitment that drives the manualwrite_noteworkflow - transferring all the noise problems onto the user. - #669, #730, #687 (all OPEN) - three separate proposals to add a sidecar that watches session transcripts and builds the knowledge graph automatically. The strongest signal that manual capture is friction. The maintainer is interested but hasn't started.
Design choices that caused the most issues
| Design choice | Cited in | Symptom |
|---|---|---|
Background async create_task for embedding sync |
#763, #578, #839 | Lost embeddings on CLI exit; silent skips; CancelledError tracebacks |
| Single permalink space per project (not per workspace) | #783, #802, #834 | Teams launch blocker; cross-workspace name collisions |
| sqlite-vec extension loaded per-session, not globally | #735, #767, #829, #658 | Vector ops fail on connections that didn't load it |
Defaulting FastEmbed cache to /tmp |
#741, #681 | Models re-downloaded every run in sandboxed environments |
| File-watcher with no liveness heartbeat | #580, #758, #798 | Silent watcher death, no recovery, missed events under hidden dirs |
Schema-validation MaxLen on relation_type |
#721 | Valid markdown fails to edit |
FastMCP AliasChoices on bool | None params |
#818 | Broken JSON schema; external clients silently drop the bool |
alembic/env.py setting BASIC_MEMORY_ENV at import |
#481 | Watch service silently disabled in production |
Inline DDL ALTER TABLE at runtime instead of migration |
#727 | Postgres deadlocks under concurrent vector sync |
Search index DELETE without project_id filter |
#256 | Edits in one project erase another project's index rows |
What the maintainers' fixes reveal
- Defaults flipped under fire: FastEmbed cache (#741),
bm resetbehavior with live MCP processes (#765 → PR #776 adds psutil guard),force_full=Trueremoved from cloud sync (#706, then again #804). - Multi-project params added retroactively to nearly every tool: PR #777, #789, #803, #807. The MCP tool surface grew a workspace dimension after launch.
- Aliases were added to make tools "training-data-friendly" (#766:
find_text/old_text/searchaliases) and then immediately brokeoverwrite(#818). Reverted in #841. - Docs heavily expanded after confusion: Postgres setup (#830 still open),
bm cloud setup(#779 was pointing users at a non-existent command). - Out-of-scope back-outs: #720 (visible_project_ids filter) closed not-fix because "would leak multi-tenant visibility concerns into a local-first single-user product".
Still-unsolved open issues - and why they're hard
- #124 git-based undo - open 11+ months. Hard because commit cadence is ill-defined and conflicts in markdown trees are nasty.
- #834 local project_id routing in mixed cloud mode - same root cause keeps spawning new symptoms.
- #382 / #686 large-context handling - pagination of search results into LLM context windows is still unsolved.
- #740 startup time - 4.6s for
--helpbecause FastMCP/onnxruntime/fastembed are imported eagerly. Multi-file lazy-import refactor not shipped. - #830 / #831 / #829 Postgres + sqlite-vec footguns - install path still surprises users.
- #669 / #687 transcript-watching sidecar - the holy grail; nobody has built it.
Concrete "do not repeat" lessons for the Rust rewrite
-
Do not background-task the indexing pipeline behind the tool reply.
write_note → return → embed latermakes the tool a liar; the nextsearch_notesmay miss the entity. (#763, #578, #839, #685). Either make indexing synchronous and bounded, or return a structuredindex_status: pending|completeso the caller can--wait. -
Bake the identity dimension in from day one.
(workspace, project, permalink)is a 3-tuple. Retrofitting it caused 12+ bugs (#782/#783/#788/#793/#799/#800/#802/#803/#804/#805/#810/#820/#834). The Rust schema should encode the full coordinate at every layer, even when you only ship single-workspace mode. -
Liveness > correctness assumptions for the watcher. A long-lived file watcher will go stale. Build heartbeats, watchdog timers, and "did we miss any events" reconciliation passes from the start. (#580, #758, #798). Treat every notify-rs-style loop as suspect.
-
Treat the embedding/vector backend as a fallible plugin, not a default-on assumption. sqlite-vec, pgvector, FastEmbed, ONNX - every one of them has bitten basic-memory (#735, #767, #741, #681, #830, #831, #658, #829, #578). In Rust, isolate the embedder behind a trait, fail loudly at startup if the backend can't load (don't silently degrade like #578), and don't default cache paths to
/tmp. -
Make
resetsafe. Unlinking SQLite while a sibling process holds the inode = mystery phantom search results (#765). Acquire an exclusive advisory lock, or psutil-style live-process check before any destructive op. -
No inline DDL at runtime, ever. #727's Postgres deadlock from runtime
ALTER TABLE. Migrations are migrations; never let "ensure table" code paths execute schema changes. -
For the manual-capture problem: the absence of explicit complaints in the tracker is a trap. The complaints are encoded as (a) repeated proposals for a transcript-watching sidecar (#669, #687, #730), (b) Cursor-pollution complaints maintainers close as not-our-problem (#297), and (c) the "what should we commit" indecision in #124. A Rust rewrite that listens to a Claude Code/Codex transcript directory and writes notes without an
@-mentionwould convert the loudest implicit pain into the headline feature.