Summary
On a project that has never been indexed, search_notes returns an empty result set for every query — including a stopword-grade term like "the" — and says nothing about the index being empty. From inside an MCP session that is indistinguishable from an honest "no such note."
That matters because "search before creating" is the standing rule in most agent workspaces. When search cannot fail loudly, that check passes vacuously: the agent searches, sees nothing, and concludes the note does not exist. It is also the state of every fresh install before the first index pass, so it is the first thing a new user hits.
Reported by a user running a ~24k-file local knowledge base across a bunch of agent workspaces. One of their machines had bm installed and never indexed, and it was the machine most of the workspaces booted from. Neither they nor their agents noticed for weeks — the agents just grepped the tree and carried on.
Reproduce
- Install Basic Memory on a machine with notes on disk, and never run an index pass (
last_indexed_at IS NULL).
- From an MCP session, call
search_notes(query="the", project="<project>").
- Result:
results: [], total: 0, with the standard "No results found for 'the' … Try broader or different terms" copy.
- Expected: something that names the state — files are present, the index was never built.
Why it happens
_format_search_markdown in src/basic_memory/mcp/tools/search.py:377-400 deliberately assumes an empty result means "no match for this query," not "empty knowledge base":
# Empty search is usually "no match for this query," not "empty knowledge base," so we
# do not repeat the first-note offer here (that would nag established users). Point at
# recent_activity, which owns the getting-started guidance when the base is truly empty.
That assumption is right for an indexed project and wrong for a never-indexed one, and nothing on the path distinguishes the two. The zero-results branch at search.py:1480-1486 only logs and returns the empty response as normal.
The handoff to recent_activity does not rescue it either: recent_activity does not consult readiness, so on a never-indexed project it is empty for the same reason. The two surfaces agree, and are both wrong together.
The signal already exists
#1414 added exactly what is needed and wired it into the CLI only:
Project.last_indexed_at (models/project.py:76-82) — "the one durable bit that separates 'never indexed' from 'indexed and idle'. Every other readiness signal is a count, and a count of zero cannot tell 'nothing to do' from 'nothing was ever started'."
ProjectIndexPhase.NEVER_INDEXED and ProjectIndexReadiness.describe() (schemas/project_readiness.py:112-143), which already renders "N files present, not yet indexed — run 'bm project index <name>'".
report_project_readiness (cli/commands/command_utils.py:102) renders it for bm project add / bm status.
grep -rn "last_indexed_at" src/basic_memory/mcp/ returns nothing. The MCP surface — the one agents actually read through — never asks.
This is the same class of bug as #1414, one surface over: #1414 fixed vacuous readiness on the CLI, this is vacuous negative results on MCP.
Suggested fix
When a search returns zero results, check project readiness before formatting the miss. If the phase is NEVER_INDEXED, say so instead of reporting a negative — reuse ProjectIndexReadiness.describe() so the wording cannot drift from bm status. Roughly:
Search returned no results because project '' has never been indexed (N files present on disk). This is not a negative result — run bm project index <name>.
Notes on scope:
Filed at b04d1b6d.
Summary
On a project that has never been indexed,
search_notesreturns an empty result set for every query — including a stopword-grade term like"the"— and says nothing about the index being empty. From inside an MCP session that is indistinguishable from an honest "no such note."That matters because "search before creating" is the standing rule in most agent workspaces. When search cannot fail loudly, that check passes vacuously: the agent searches, sees nothing, and concludes the note does not exist. It is also the state of every fresh install before the first index pass, so it is the first thing a new user hits.
Reported by a user running a ~24k-file local knowledge base across a bunch of agent workspaces. One of their machines had
bminstalled and never indexed, and it was the machine most of the workspaces booted from. Neither they nor their agents noticed for weeks — the agents just grepped the tree and carried on.Reproduce
last_indexed_at IS NULL).search_notes(query="the", project="<project>").results: [],total: 0, with the standard "No results found for 'the' … Try broader or different terms" copy.Why it happens
_format_search_markdowninsrc/basic_memory/mcp/tools/search.py:377-400deliberately assumes an empty result means "no match for this query," not "empty knowledge base":That assumption is right for an indexed project and wrong for a never-indexed one, and nothing on the path distinguishes the two. The zero-results branch at
search.py:1480-1486only logs and returns the empty response as normal.The handoff to
recent_activitydoes not rescue it either:recent_activitydoes not consult readiness, so on a never-indexed project it is empty for the same reason. The two surfaces agree, and are both wrong together.The signal already exists
#1414 added exactly what is needed and wired it into the CLI only:
Project.last_indexed_at(models/project.py:76-82) — "the one durable bit that separates 'never indexed' from 'indexed and idle'. Every other readiness signal is a count, and a count of zero cannot tell 'nothing to do' from 'nothing was ever started'."ProjectIndexPhase.NEVER_INDEXEDandProjectIndexReadiness.describe()(schemas/project_readiness.py:112-143), which already renders"N files present, not yet indexed — run 'bm project index <name>'".report_project_readiness(cli/commands/command_utils.py:102) renders it forbm project add/bm status.grep -rn "last_indexed_at" src/basic_memory/mcp/returns nothing. The MCP surface — the one agents actually read through — never asks.This is the same class of bug as #1414, one surface over: #1414 fixed vacuous readiness on the CLI, this is vacuous negative results on MCP.
Suggested fix
When a search returns zero results, check project readiness before formatting the miss. If the phase is
NEVER_INDEXED, say so instead of reporting a negative — reuseProjectIndexReadiness.describe()so the wording cannot drift frombm status. Roughly:Notes on scope:
recent_activity, since search currently delegates its "truly empty" guidance there.output_format="json") callers need the signal too, not just the markdown string — an agent parsing JSON seesresults: []with no hint. A field on the response (e.g.index_phase) beats prose for that path.reindex --embeddingssilently no-ops on an unsynced project), Vector search silently degrades when configured index has no ready manifest rows #1236 (vector search silently degrades with no ready manifest rows), [BUG] Full-text search indexes only the first ~5,600 chars of a note; deep content is unreachable in all search modes and zero-results report total_is_exact: true #1320 (zero-results reportingtotal_is_exact: true). Same theme — a search path that cannot distinguish "nothing matched" from "nothing was searched."Filed at
b04d1b6d.