Operate
Troubleshooting
Daemon not running, embeddings stuck at zero, extension says "not signed in" — and the actual fixes.
Run personkit doctor first — it diagnoses most of what’s below. The rest of this page is the common dead-ends with explicit fixes.
”Last event was N days ago”
Reported by personkit doctor and the corpus health output.
Cause. The harvester daemon isn’t running. Pre-v0.3 builds shipped a launchd job for observers only; harvesters had to be invoked manually.
Fix.
personkit daemon --install
launchctl list | grep one.aiperson.personkit-daemon # confirm loaded
tail -f ~/.dotperson/daemon.out.log # watch the first cycle
If the job won’t load, the install path prints the manual launchctl load command and points you here.
”Embeddings: 0” in corpus health
Cause. No embedder configured. Vec retrieval degrades to BM25-only; FTS still works.
Fix. Pick one:
- Vertex (cloud): set
GCP_PROJECT_ID(and eitherGOOGLE_ACCESS_TOKENfor one-shot use or run on a host with Workload Identity / ADC). - fastembed (offline, ~120 MB ONNX model): rebuild with
cargo install --features corpus/fastembed personkit, or wait for the next release where this becomes the default.
Then backfill the existing events:
personkit corpus embed --max 20000
The daemon drains pending embeddings on every cycle once an embedder is configured.
”Browser extension says: not signed in”
Cause. The dashboard handed back a token to the extension that has since expired (Firebase ID tokens live ~1h; the extension lazily refreshes via the dashboard handoff).
Fix. Click “Sign in” in the popup; the dashboard tab will re-mint a fresh token via externally_connectable and the popup will live-update.
If the dashboard never returns the token, check that the dashboard origin is https://aiperson.one and the extension has externally_connectable.matches containing it (it does, by default). If you’re testing on a different domain, add it to the manifest.
”Daemon is running but nothing new is showing up”
personkit doctor reports the daemon as loaded, but corpus health doesn’t change.
Possible causes.
- Harvesters disabled. Check
~/.dotperson/harvesters/enabled.json. Reset withpersonkit harvest enable <id>for each one (or delete the file — default-on). - No sources detected.
personkit harvest run-onceshows each harvester’sis_present()decision. If the source path doesn’t exist (no~/.claude/projects/, no git repos in the watched list), the harvester skips. - Watermark stuck. A bug-in-progress can leave a watermark JSON at
~/.dotperson/harvesters/<id>.jsonreporting “already up-to-date” forever. Delete the file to force a re-scan.
”MCP host says: tool not found”
Symptom: Claude Code / Cursor / etc. doesn’t see read_persona or corpus_query.
Cause. The MCP server isn’t wired into the host’s config, or npx -y -p @aiperson/mcp-server@latest dotperson-mcp is failing.
Fix.
personkit connect --tool claude_code # or cursor / vscode / windsurf / …
personkit verify surfaces # runs a stdio MCP smoke against every wired host
If verify surfaces fails with spawn dotperson-mcp ENOENT, your host doesn’t inherit shell PATH. Set PERSONKIT_BIN to the absolute path of personkit in your shell config and reconnect.
”Browser extension doesn’t capture anything”
Cause. Capture is opt-in per surface. The popup shows a per-surface toggle for each of the 21 wired chat surfaces; every one is off by default until you click it. (Chinese-frontier surfaces — Kimi, DeepSeek, Qwen, Doubao, ChatGLM, Wenxin, Yi, Hailuo, SenseChat — additionally sit behind an “Experimental surfaces” master flag.)
Fix. Flip the toggle for each surface you want covered. Refresh the chat tab. Check the popup’s per-surface health dot — green within five minutes of the next turn means it’s landing. From the terminal:
personkit corpus surfaces # confirm the surface appears
personkit corpus messages --surface=browser-ext:chatgpt --limit=5
If the dot stays red and the popup shows non-zero “rejected” counts, capture is reaching the relay but the relay is rejecting it — usually a stale extension version (refresh the unpacked extension or wait for the Chrome Web Store update). If the dot is red AND rejected is zero, the extension never reached the relay — check the popup for a “Sign in again” banner (a 401 wipes the token rather than re-queuing indefinitely).
”Synthesis pass keeps reporting threads_summarised: 0”
Cause. No threads in the window. The daily brief and synthesis read the V2 threads table; if your only capture is via the legacy events (kind = claude_session etc.) and you haven’t upgraded claude_sessions to emit messages, the threads table stays empty.
Fix. Upgrade. The v0.3+ claude_sessions harvester emits both — re-run personkit harvest run-once --id claude_sessions and re-check corpus health.
”Relay sync push failed: 401”
Cause. ID token expired and refresh failed (often because the Firebase refresh token was rotated server-side).
Fix.
personkit login
Re-runs the GitHub → Firebase flow, writes fresh credentials, restarts sync on the next daemon cycle.
Resetting from scratch
If something is so off you want to start over:
# Archive first — two formats, your choice:
personkit corpus snapshot --out ~/dotperson-archive # signed JSONL (round-trippable)
personkit export --format=markdown --out ~/dotperson-archive-md # plain markdown (Obsidian)
# Then wipe — pick how thorough:
personkit corpus wipe --keep-persona # just the corpus DB; credentials + persona cache stay
personkit corpus wipe # corpus + harvester/observer state + persona cache
personkit uninstall --yes # full uninstall: also removes the daemon plist
# Re-run setup:
personkit setup
personkit daemon --install
Your persona repo on GitHub survives — it’s yours. Restore the corpus from an archive any time with:
personkit corpus restore --from ~/dotperson-archive
Reporting bugs
If none of the above match, capture context:
personkit doctor --errors --since 1d > /tmp/diag.txt
personkit corpus health --json >> /tmp/diag.txt
tail -200 ~/.dotperson/daemon.err.log >> /tmp/diag.txt
Open an issue with /tmp/diag.txt at github.com/humphreytheodore/personkit/issues.