Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cad0p/pi-napkin/llms.txt

Use this file to discover all available pages before exploring further.

When something goes wrong with pi-napkin, the best starting point is the vault and the distill error directory. Most issues fall into one of the categories below — find the symptom that matches yours, expand it, and follow the steps. For anything that requires inspecting distill subprocess state, the Diagnostic Commands section at the bottom has quick copy-paste one-liners.
Some command or tool couldn’t resolve a vault from the current directory. pi-napkin (and the napkin CLI) walks up from cwd looking for a .napkin/ config subdir. If that walk finds nothing, it falls back to the global config at ~/.config/napkin/config.json. If that’s also missing, vault resolution fails with this message.Solutions:
  • Run from inside a vault. Any ancestor directory containing .napkin/ (or .obsidian/) will satisfy vault resolution — you don’t need to be at the vault root itself.
  • Set the global fallback. Create (or edit) ~/.config/napkin/config.json:
    {
      "vault": "~/path/to/vault"
    }
    
  • Pass --vault explicitly. Every napkin subcommand accepts a --vault <path> flag to bypass resolution entirely.
Confirm vault resolution is working:
napkin vault --json
This prints the resolved configPath and contentPath. If it errors or points at the wrong directory, one of the three fixes above is needed.
Auto-distill silently stops scheduling new runs when the background state becomes inconsistent — most commonly because a previous pi process crashed mid-distill and left a stale worktree entry behind.Step 1 — Check for stale worktrees: Run /distill-status in your pi session. If you see entries marked (dead), those are the culprits. The next session start will sweep them automatically via cleanupStaleWorktrees, so simply restarting pi often fixes this.Step 2 — Nuke the per-vault cache: If restarting doesn’t help, delete the cached state for the affected vault:
# Find the vault hash from `napkin vault --json` (sha256 of contentPath, first 16 hex chars)
rm -rf ~/.cache/napkin-distill/<vault-hash>/

# Nuclear option — clears all vaults:
rm -rf ~/.cache/napkin-distill/
This is safe to run at any time. Anything valuable is either already committed to the vault’s default branch or was never going to commit. The cache only holds ephemeral worktree state.After clearing, open a new pi session and auto-distill will re-arm on the next session_start.
Each failed distill writes two files into <vault>/.napkin/distill/errors/:
  • Outcome sidecar: <ISO-timestamp>-<pid>-<branch-hash>.outcome — first line is the machine-readable class (failed:<reason>); remaining lines are a human-readable recovery hint pointing at the exact command to run.
  • Error log: <ISO-timestamp>-<pid>-<branch-hash>.log — wrapper diagnostics plus the agent’s stderr.
Always read the outcome file first — the recovery hint tells you exactly what happened and what to do.Common patterns:
SymptomCauseFix
agent-timeout recurringDistill is taking longer than distill.maxDurationMinutes (default 10)Bump distill.maxDurationMinutes in .napkin/config.json, or check the .log file to see what the agent was doing when it was killed
markers-after-agent-exitThe agent left unresolved conflict markers in a tracked *.md file; the squash commit may already be on the default branchRun git revert HEAD --no-edit in the vault to undo the corrupt commit cleanly. Content is recoverable from git reflog for ~90 days
agent-exit-nonzeroThe pi -p subprocess exited non-zeroCheck your model provider (rate limits, auth refresh, network). The agent’s full stderr is in the companion .log file
divergent-historyA teammate pushed to origin/<default> while this distill was running, so local and remote divergedRun git pull --no-rebase in the vault to integrate, then let the next distill run normally
pre-existing-markersConflict markers were already in the vault before this distill ran — the agent didn’t introduce themFind the files listed in the .log, resolve the markers by hand, commit, then re-run distill
head-not-on-defaultThe vault HEAD ended up on the wrong branch after the agent exitedRun git checkout <default-branch> in the vault, confirm nothing is in flight, then re-run distill
internal-validator-errorThe wrapper’s post-distill marker validator could not run (e.g. mktemp failed due to a full disk or locked TMPDIR), so the vault was never scanned after the agent exitedInspect the vault manually for unresolved <<<<<<< / ======= / >>>>>>> markers before relying on the squash commit. If clean, the squash is keepable; otherwise git revert HEAD --no-edit. Content is recoverable from git reflog for ~90 days
The outcome sidecar’s recovery hint is always specific to the failure. It names the exact git revert SHA or git reflog command rather than generic advice — read it before reaching for a nuclear option.
Auto-distill has two hard prerequisites: git and the subdir vault layout (.napkin/config.json in a .napkin/ subdirectory, distinct from the notes root). If either is missing you’ll see one of these errors.vault not a git repo:Three options — pick any one:
  • Set distill.enabled: true in .napkin/config.json and let pi-napkin auto-init git on the next session_start. It will run git init, scaffold the managed .gitignore block, and make an initial commit.
  • Run git init in the vault root yourself, then start pi.
  • Opt out entirely: set distill.enabled: false in .napkin/config.json.
Note: manual /distill does not require git — it falls back to a tmpdir spawn. Only auto-distill (interval + shutdown) needs git.legacy embedded layout:This means your vault has config.json directly at the vault root (where configPath === contentPath), with no .napkin/ subdirectory. Auto-distill needs the subdir layout for worktree-based concurrency to be safe.Auto-distill is disabled for the session, but manual /distill continues to work regardless.Follow the vault migration steps to move to the subdir layout. The migration is a three-step file move:
# From your vault root (where config.json currently lives):
mkdir .napkin
mv config.json .napkin/config.json
# Edit .napkin/config.json and add at the top level:
#   "vault": { "root": ".." }
# Then reload pi (or /quit and restart).
Verify with napkin vault --json — the path field should point at <vault>/.napkin/.
The status bar shows distill: off when distill.enabled is false (or absent) in the vault config that pi actually loaded.Check 1 — Correct config location:The config must be in .napkin/config.json inside your vault:
{
  "distill": {
    "enabled": true
  }
}
The old location ~/.pi/agent/napkin.json is no longer read. If you’re coming from an older pi-napkin install, migrate:
mkdir -p ~/.config/napkin
cp ~/.pi/agent/napkin.json ~/.config/napkin/config.json
Check 2 — Vault resolution:Confirm pi is loading the config you edited:
napkin vault --json
The path field shows which .napkin/ directory was resolved. If it points somewhere unexpected, check the vault resolution order (local .napkin/ → global ~/.config/napkin/config.json).Check 3 — Session suppression:The status shows distill: off (session) when the timer has been suppressed for this specific session via /distill-auto-this-session off. Check current state:
/distill-auto-this-session status
If suppressed, re-enable with /distill-auto-this-session on.
A no-content outcome is a valid no-op — not an error. The distill agent deliberately skips sessions with nothing worth capturing.The distill prompt instructs the agent to be selective: only extract knowledge useful to someone working on the project later. It explicitly skips meta-discussion, tool output, chatter, and sessions that are purely operational (running commands, browsing files) with no conceptual content.What to check:
  • Does the session contain real knowledge content — decisions, explanations, solutions, discoveries — or was it mostly tool invocations and back-and-forth?
  • If you believe there was content worth capturing, you can trigger a manual distill immediately:
    /distill
    
    This bypasses the size-dedup check and re-runs the full distill pipeline against the current session.
no-content surfaces as a warning in the status bar (⚠) rather than an error (✗) precisely because it’s an expected outcome for short or operational sessions.
If pi-napkin surfaces config.json is not valid JSON, it means .napkin/config.json has a syntax error that prevents parsing. pi-napkin refuses to guess the intended shape — auto-distill is disabled for the session until the file is fixed.Fix it by hand:Common JSON mistakes:
  • Trailing commas after the last item in an object or array
  • Unquoted string values (all strings must be in double quotes)
  • Missing closing braces or brackets
  • Single-quoted strings instead of double-quoted
Validate before saving:
# Python's built-in JSON parser (available everywhere):
python3 -m json.tool <vault>/.napkin/config.json

# Or using jsonlint (install via npm or brew if needed):
jsonlint <vault>/.napkin/config.json
Alternatively, paste the file contents into an online JSON validator such as jsonlint.com.After fixing, restart pi (or /quit and reopen the session). pi-napkin re-reads the config on every session_start.

Diagnostic Commands

Quick reference for inspecting vault and distill state from the shell:
# Check which vault pi-napkin is using and verify config resolution
napkin vault --json

# Show active background distill processes for the current vault
# (run inside a pi session as a slash command)
/distill-status

# List all distill outcome and error log files
ls <vault>/.napkin/distill/errors/

# Read the latest outcome sidecar (first line = outcome class)
cat <vault>/.napkin/distill/errors/<latest>.outcome

# Read the companion error log (wrapper diagnostics + agent stderr)
cat <vault>/.napkin/distill/errors/<latest>.log

# Nuke stuck state for a specific vault (safe — see above)
rm -rf ~/.cache/napkin-distill/<vault-hash>/

# Check which config file napkin loaded and its vault paths
napkin vault --json | python3 -m json.tool
The <vault-hash> in the cache path is the first 16 hex characters of the SHA-256 of your vault’s contentPath. Run napkin vault --json and look at the path field, then compute echo -n "<contentPath>" | sha256sum | cut -c1-16 if you need to find it manually.

Build docs developers (and LLMs) love