Troubleshooting
The MCP server refuses to start, exit 2
The harness is unset or unknown. Each server is bound to exactly one harness
by HARNESS in the registration snippet’s environment block, or --harness <name> on the mcp subcommand:
refusing to start MCP server: harness not defined. Set HARNESS=<vibe|codex|claude|opencode>
in the MCP config environment or pass --harness <name>.
refusing to start MCP server: unknown harness 'vibee'. Valid values:
vibe|codex|claude|opencode. Set HARNESS=<vibe|codex|claude|opencode> in the MCP
config environment or pass --harness <name>.
The server prints the reason to stdout and stderr and exits 2, because a server that starts with an unknown harness would bind every tool to a nonsense store.
401 “Incorrect API key” from prior_period_summary
The MCP server’s environment block is missing INCEPTION_API_KEY. List,
profile, extract, index and search all work without a key — no LLM call — and
the LLM-backed tool fails with the vendor’s 401.
Add the key to the snippet’s environment block, per
MCP setup, or export it in the shell
that launches the client so the server inherits it. A .env file is not a
reliable route for a spawned server: it is only found relative to a working
directory the MCP client chooses.
The same 401 on a CLI invocation means the key is neither exported nor in a
.env in the current directory.
“LLM provider mercury is not compiled into this build”
The binary was built with --no-default-features, or without that vendor’s
feature. Either rebuild:
cargo build --release --features mercury
or keep the vendor-free build and use the log-mining tools, which need no key.
The MCP tool name stays in tools/list either way — clients bind by name, so
a missing vendor is reported on the call, not by a disappearing tool. See
Configuration.
unknown LLM provider '<name>'
The name is not a vendor. Valid values are mercury and mistral; an
unrecognised name is refused rather than silently falling back to Mercury.
The sandbox guard refuses to build an adapter
TOTAL_RECALL_SANDBOX is set but TOTAL_RECALL_OPENCODE_ROOT is not: refusing to
read the live store. Point TOTAL_RECALL_OPENCODE_ROOT at a fixture or scratch
copy.
TOTAL_RECALL_SANDBOX=1 is armed and this harness has no root override. Set
the harness’s override — see
Configuration — or unset the
sandbox. The guard never falls back to the live store.
An override resolves to nothing usable
An override that does not exist, or is empty, is an error naming the path — not
a silent fall-back to $HOME. Check for a typo in the variable name and in the
path, and remember an empty value counts as unset.
“No sessions found”
The store resolved to an empty or non-existent directory. Check, in order: the
HARNESS value, then the store root the tool resolved for it (see
Configuration), then that the
harness has actually written sessions there.
The CLI exits 2 with a path in the message
A rollout payload could not be resolved or read. Read paths return
Result<T, String>: unreadable data is an error, never an empty session. The
usual causes are permissions on the store, or the session id not resolving.
A JSONL line that fails to parse is not fatal — it is skipped with a WARN
carrying its line number, visible under --verbose.
list_sessions is the deliberate exception: a session whose payload cannot be
read is still listed, with counts from whatever could be read, and carries a
read_error field.
A tool reports “No sessions found” but the store has sessions
A partial session_id that matches nothing resolves to no session, and the
tools answer with that error rather than guessing. Confirm the id against
list_sessions; partial ids are matched by substring, and the most recent
match wins.
do_android_dream_of_electric_sheep reports “not indexed”
The session has no shadow index yet. Run index_sessions (or
total-recall --harness <h> index) first — the search reports unindexed
sessions in its header and skips them, rather than returning nothing as if the
terms were absent. A corrupt index is reported in the header, also not fatal.
she_said_he_said_action fails on a non-OpenCode harness
The tool is implemented for OpenCode only; other harnesses return a clear unsupported error. Matching runs inside SQLite as a custom scalar function on a read-only connection, which is an OpenCode-store property.
A tool name looks wrong in the agent’s surface
The MCP server key was renamed. The tool names derive from it —
total-recall_list_sessions, … — so keep the key as total-recall in both
registration snippets.
Extraction came back truncated
Expected: the MCP tools are bounded by design. Read bounds.truncation_reason
and bounds.notice — record_limit, byte_cap or record_clamp — and page
with offset from 0, following bounds.next_offset. See
Tools.
CLI extraction is unbounded unless you pass --limit / --max-bytes; when you
do, the truncation notice goes to stderr so stdout stays machine-parseable.
“even one record … exceeds max_bytes”
One record is larger than the whole budget. Raise max_bytes, or set
max_record_bytes to enable the per-record clamp. The tool errors rather than
silently breaking the cap.
The store is huge and list is slow
A scoped listing is bounded where the data lives: the window, the directory
substring and the row cap are pushed into the store query, so a scoped
list costs what it asks for, not what the store holds. An unscoped list
is the full aggregate scan over the whole store — a disk-read question, not
a CPU one. A store that grew without bound is the subject of the
store-vacuum runbook.
held_back is most of the window
Expected: list_sessions renders as many of the window’s most recent rows as
max_bytes holds, and states the rest in held_back. Each row carries at most
the 10 most recent child_sessions ids and the 10 first aliases, with
child_session_count / alias_count stating the true totals and the row’s
notice naming what it held back — a bounded row means the count is set by the
budget, not by any session’s size. Raise max_bytes for a wider slice, or
narrow the window with hours_back or directory.
“unrecognised field hour_back”
A parameter name the tool does not accept. Every tool rejects unknown fields by name and lists the fields it does accept: a typo is a silently unscoped call otherwise, so the call is answered with the accepted names rather than run on the default scope.
“limit is out of range” / “max_bytes is out of range”
The value is outside the bound the tool enforces — limit runs 1 to 1000,
max_bytes 1 to the 8 MiB ceiling. Either value is stated in the tool’s
schema as minimum/maximum, and 0 means “omitted” for both: limit: 0
takes the default 100, max_bytes: 0 takes the tool’s default. Page with
offset/limit instead of raising limit.