Troubleshooting¶
Find your symptom, read the message exactly as it appears, and follow the fix. Start with the command that checks everything:
sb doctor
Each line says ok, WARN or FAIL, and what to do under anything that is not fine. Most problems on this page show up there first. If yours is not here, open an issue and paste the output of sb doctor (it contains no secrets).
On this page: install and start, the model, sources, asking, the nightly job, mail, git and backup, files, disks and unusual setups.
Install and start¶
sb is not found¶
sb: command not found
The folder that holds sb is not on your PATH. With uv, run uv tool update-shell and open a new terminal. sb doctor shows WARN global install: sb is not on your PATH for the same reason. See Install.
The package cannot be imported (macOS hidden files)¶
ModuleNotFoundError: No module named 'esbi_cli'
This only happens when you run from a source checkout with uv sync, and the environment lives in ~/Documents, where iCloud keeps marking .venv files hidden (Python 3.13 then skips them). Keep the environment outside it, for good:
export UV_PROJECT_ENVIRONMENT="$HOME/.local/share/venvs/esbi-cli"
then uv sync again. The quick fix is chflags -R nohidden .venv. A normal install with Homebrew or uv tool install is not affected.
No config file is found¶
error: No config.toml found (looked in: ...)
sb doctor shows FAIL config. You have not run sb init, or the config is somewhere else. Run sb init, or point at the file with --config PATH or the ESBI_CONFIG variable. sb info prints the file in use. A config.toml in the current folder is never read, on purpose. If you pass --config (or set ESBI_CONFIG) with a path that does not exist, the command stops with error: config file not found: PATH; only the default places are tried one after the other.
The config has a mistake¶
| Message | Fix |
|---|---|
error: config.toml needs [paths].vault |
Add vault = "..." under [paths] |
error: [run].nightly_time must look like HH:MM (24 hours), got '25:00' |
Write the time as "03:00" |
error: [notes].language must be one of: en, es, got 'fr' |
Use a language the program has; to add one, see Add a language |
error: [notes].viewer must be "obsidian" or "none", got ... |
Use one of the two |
error: Expected ']' at the end of a table declaration (at line 2, column 7) |
A TOML syntax error: the line and column are in the message |
error: [llm.summarize] has an unknown key 'num_ctxx' (did you mean 'num_ctx'?). Valid keys: ... |
A key is misspelled. The message names the section, the key and the valid keys. The same check covers every section |
error: [run].max_chunks must be a whole number, got 'ten' |
A value has the wrong type (text where a number goes, "true" where true goes). Fix the value as the message says |
error: config file not found: PATH |
The file given with --config (or ESBI_CONFIG) does not exist. Check the path |
The vault is not found¶
error: vault not found: ~/Documents/Obsidian/esbi (run `sb init`, or fix [paths].vault)
[paths].vault points at a folder that does not exist: a typo, an unmounted drive, a cloud folder not synced yet. Every command that opens the vault stops here and creates nothing (before, it made the folder and a .esbi/ inside it, so a mistyped path became a second, empty vault). With --json the error object has the code vault_not_found. Fix the path, or run sb init --vault PATH. sb doctor, sb init and the commands that only edit the config (sb ocr ..., sb email set-password) still work without it, so sb doctor is the way to see what is wrong.
The vault is not recognised¶
FAIL vault: ~/Documents/Obsidian/esbi is not a esbi-cli vault (SCHEMA.md or wiki/ missing)
fix: fix [paths].vault in config.toml
vault points at a folder that sb init did not make. Fix the path, or run sb init --vault PATH to create what is missing. After a restore from git the same message appears until you run sb init (Back up and restore).
No matching distribution for pypdfium2¶
No solution found when resolving dependencies:
... pypdfium2 ... no matching distribution found ...
esbi-cli reads PDFs with pypdfium2, and its prebuilt packages exist only for macOS 13 or later (and Linux, which is not a supported system). On macOS 12 or older the installer finds no package for your system and stops. Update macOS (there is no other PDF engine to fall back to), or install esbi-cli on a machine that runs macOS 13 or later.
The shell says no matches found¶
zsh: no matches found: https://example.com/post?id=7
A link with ? or & must be in quotes: sb add "https://example.com/post?id=7".
The model¶
The model is not reachable¶
The LLM is unreachable (is Ollama running?). Nothing was lost; sources stay queued.
FAIL model summarize: Ollama is not reachable at http://localhost:11434
fix: brew services start ollama (or open the Ollama app)
Ollama is not running, or base_url is wrong. Start it (brew services start ollama, or open the app) and check curl http://localhost:11434 answers Ollama is running. The run stopped and nothing was counted against your sources: run sb run again. The daily index is still rebuilt after a failed run, and it tells you in the morning: the first line under To review says the model server was unreachable in the last run and how many sources are waiting, and sb doctor shows WARN last run with the same fix. Both go away after a run that reaches the model.
The model is not installed¶
FAIL model summarize: ollama/llama3.2:latest is not installed
fix: ollama pull llama3.2:latest
Download it: ollama pull llama3.2. ollama list shows what you have.
LM Studio does not answer¶
FAIL model summarize: LM Studio is not reachable at http://localhost:1234/v1
fix: open LM Studio, load a model, and start the local server (Developer tab)
FAIL model summarize: LM Studio does not serve 'x'
fix: use one of: qwen2.5-7b-instruct
Start the server, and use the identifier lms ls shows in model = "lmstudio/...".
The subscription does not work¶
| Message | Fix |
|---|---|
FAIL model ...: the claude command is not installed |
Install Claude Code |
FAIL model ...: claude is not logged in |
claude auth login |
claude: ... Run claude auth login. (during a run) |
Your login expired: claude auth login |
claude: ... with a usage-limit message |
You spent a usage window of your plan. The run stopped like any model outage, nothing is lost, and the next run resumes. Set a local fallback to keep going meanwhile |
claude gave no answer within 300s |
A timeout for one source; raise timeout_seconds in that [llm.*] section |
claude returned no JSON (exit 1): ... |
The claude tool failed to start; run claude by hand to see why |
The ChatGPT plan does not work¶
| Message | Fix |
|---|---|
FAIL model ...: the codex command is not installed |
Install OpenAI's codex tool |
FAIL model ...: codex is not logged in |
codex login |
codex (exit 1): ... Run codex login. (during a run) |
Your login expired: codex login |
codex (exit 1): ... with a usage-limit message |
You spent a usage window of your plan. The run stopped like any model outage, nothing is lost, and the next run resumes. A local fallback keeps it going meanwhile |
codex gave no answer within 300s |
A timeout for one source; raise timeout_seconds in that [llm.*] section. Each call also carries about 8,500 tokens of the tool's own overhead, so it is slower than it looks |
codex (exit 1): Unknown feature flag ... |
Your Codex is a different version from the one the worker was checked against (0.146.0) and no longer knows a feature the worker switches off. Update esbi-cli; or open an issue with the message |
An API key is missing¶
FAIL model summarize: anthropic/claude-sonnet-5-5: $ANTHROPIC_API_KEY is not set
fix: export ANTHROPIC_API_KEY=...
During a run the message is Environment variable ANTHROPIC_API_KEY is not set. Set the variable in your shell profile and open a new terminal. A nightly job started by launchd does not read your profile, so for unattended runs prefer a local model or the subscription. A rejected key shows HTTP 401 in the message.
The model name is not understood¶
FAIL model summarize: unknown provider in 'foo/bar'
fix: use ollama/, lmstudio/, openai/, anthropic/, claude-cli/ or codex-cli/
Other forms: Model must look like '<provider>/<name>', and Unknown LLM provider 'x'; use one of [...]. Write the model as provider/name.
There is no summarize section¶
error: 'No [llm.summarize] section in config'
[llm.summarize] is required. Add it (Configuration), or run sb init for a fresh config.
The model is too slow, or times out¶
A line like .../api/chat gave no answer within 300s means one chunk or source took longer than timeout_seconds. It fails that source, not the run. Options: raise timeout_seconds; set max_tokens on the model that reads chunks (small models sometimes loop, and the cap turns a 5-minute timeout into seconds); use a smaller max_source_chars; or a faster model. Expect 3 to 15 minutes per source with a small local model.
Notes come out in the wrong language or thin¶
The model is too small. The worker already retries once when the note comes out in the wrong language, and says The model answered in the wrong language (wanted English) when the second try fails too. Try a larger model for [llm.synthesize] (Choose how the notes are written) and compare with sb bench. Rebuild old notes with sb reingest.
Sources¶
A source keeps failing¶
sb run prints one ! line per failed source with the attempt and the reason; sb status shows each parked source and its last error. A source is tried three times (it is queued while it is being retried), then parked as failed and listed under To review in the daily note. Fix the cause, then sb retry; or sb drop it. Stopping a run yourself is not a failed attempt: after Ctrl-C or SIGTERM the source that was being read goes back to the queue with no attempt counted, so interrupting as often as you like never parks a good source (older versions counted it, and three interruptions parked one).
| Last error | What it means and what to do |
|---|---|
No readable article content found at URL |
The page is a login wall, mostly scripts, or very short. Clip it with the Web Clipper |
Could not fetch URL: Client error '404 Not Found' ... |
A dead link: sb drop it |
Could not fetch URL: ... (a connection error) |
No network, or the site is down. sb retry later |
Could not fetch URL: Cannot resolve HOST: ... |
The name does not exist, or DNS failed |
Refused to fetch URL: localhost resolves to a non-public address (127.0.0.1) |
By design, only public addresses are fetched. Save the page as a Markdown file and drop it in inbox/ |
Refused to fetch URL: Only http(s) URLs are fetched, not 'ftp' |
Only http and https |
Refused to fetch URL: The response is larger than 20 MB or ...: The download took more than 120 s |
Downloads are capped; download the file yourself and add it by path |
Refused to fetch URL: Too many redirects (more than N) |
The link loops |
PDF has almost no extractable text (scanned?): add an [llm.ocr] model ... |
A scan: turn on OCR |
PDF has no readable text, even with OCR |
The OCR model found nothing on any page |
NAME has no readable text (a photo or a diagram?) |
An image with fewer than 40 characters of text. The worker does not describe pictures. sb drop it |
The OCR model could not read NAME: ... |
The vision model failed: check sb doctor (model ocr) |
Could not open PDF: ... / Could not open NAME as an image: ... |
The file is damaged, not what its extension says, or a PDF that needs a password (Could not open PDF: Failed to load document (PDFium: Incorrect password error).). Remove the password (open it in a PDF viewer and export a copy) and add that |
| A PDF note has long runs of short label-like lines, or too few or too many headings | A chart-heavy PDF (a slide deck, a report full of plots): the labels of a chart are text in the PDF. The reader drops a short line that the file repeats 8 times or more, short lines packed into one spot of a page and long runs of label-like lines, and keeps at most 8 headings per page (at least 40 per file), the numbered and the biggest first, so a few chart labels can still come out as lines or as headings. The note is still made; a larger model copes better, and the text inside images and charts is not read at all. See How a note is made |
NAME has almost no text (N chars) |
A clip with under 40 characters of text after the page chrome of a GitHub, LinkedIn, X, Reddit or YouTube clip was removed. A clip of a login wall, a cookie page or an empty feed is exactly this: nothing but chrome. Open the page again logged in and re-clip it; sb drop the empty one |
Unsupported file type: .docx |
Only .pdf, .md and images (with OCR) are read; convert it |
the run died while reading this |
The process was killed while reading this source three times in a row (a huge PDF running out of memory). Try a smaller file. A run you stopped with Ctrl-C does not count: see above |
Warnings after a note is written¶
These are not errors: the note was written, and the line says what was left out. They appear after the + Title line of sb run (and as warning: for sb ingest).
| Warning | Meaning |
|---|---|
Dropped 2 diagram edges: an end was not in the source or the note. |
The model drew a relation whose end is neither in the source text nor a concept, entity, term or alias of the note, so the worker removed it. Often the concept map is then left out (fewer than two relations remain). Nothing to do; a stronger model invents fewer |
Only half of chunk 2 of 5 could be read. |
A chunk of a long source was invalid twice, was read again in two halves, and only one half worked. The note covers the rest of the source and a little less of that chunk. sb reingest TITLE tries again |
Removed N lines of page chrome from the clip. |
A clip of GitHub, LinkedIn, X, Reddit or YouTube had menus, sidebars, sign-in or cookie lines; they were removed before reading and counted as stripped_lines in the note. The clip in raw/inbox/ is as it came. Nothing to do; if real text was lost, the original is there |
Not read: 12 of 52 parts of the source (the last 23% of the text, starting at "...") because the limit of 60 model calls per source ([run].max_calls_per_source) was reached. |
A very long source (a book) needed more model calls than [run].max_calls_per_source allows, so the reading stopped and the rest is not in the note. The note starts with the same warning (> [!warning] Not read: ..., No leído: ... in Spanish), so you can see it when you read it. Raise the cap in [run] (a source of 40 chunks takes about 55 calls) and rebuild the note with sb reingest; it costs more tokens and time |
Section 2 was merged without the model (...) |
In a long source, the model could not merge the notes of a group of chunks (invalid twice, too slow, or no calls left), so code merged the points of that section instead. Nothing was invented; the section is just less condensed |
Removed 2 link or image address(es) the model wrote that the source does not contain. |
The model wrote web addresses (links or images) that are not in the source, so the worker removed them and kept the link text. Often a small model inventing a reference; sometimes a hostile page asking for it. Nothing to do. See Prompt injection |
Could not read chunk 2 of 5; skipped (...) |
Both halves failed too (or the model timed out): that part of the source is not in the note. Try a larger model for [llm.summarize] |
Another run is in progress¶
Another run is in progress; skipping.
Another sb run (or the nightly job, or sb reingest) holds the lock. It ends by itself, and the lock is released even after a crash. sb run exits 0 here; sb reingest says Another run is in progress; try again when it finishes. and exits 1.
Files in inbox are not read¶
Not read, esbi-cli cannot read these files in inbox/: letter.docx, slide.png
Images: reading images and scanned PDFs is off: `sb ocr enable` sets up a local vision model ([llm.ocr], https://rubenamaury.github.io/esbi-cli/docs/reference/configuration/)
Those files stay in inbox/ and are listed on every scan. Images need OCR; HEIC, Word and other types are not supported: convert them to JPG, PDF or Markdown.
A source says it is already in the wiki¶
Already in the vault as [[Zettelkasten]] (use --force to redo).
Not an error: the same URL or the same text is already a note. See If a source already exists. sb add says Skipped URL: already in the wiki as [[...]].
Reading images is slow, or DeepSeek-OCR never finishes¶
sb ocr status (and sb doctor, WARN ocr memory) says when the model you chose wants more memory than this machine has. deepseek-ocr:3b is a model for 16 GB of memory or more: on an 8 GB Mac it loads at about 10 GB, takes minutes for one small image, and was measured about 100 times slower than the recommended one. Switch with:
sb ocr enable --model qwen3-vl:2b-instruct --pull
A WARN ocr ollama line means Ollama is older than the model needs: brew upgrade ollama, or update the app. See Choosing the OCR model.
An email is refused¶
an email cannot be written by a model that sends text away: add a local model as [llm.private] in config.toml
You have a cloud model and no local [llm.private]. Add it (Keep email local). The related [llm.private] must be a model that runs on this machine, not one that sends text away means your private model is remote.
Asking¶
sb ask finds nothing but you expect an answer¶
I find nothing about this in the wiki.
The model cited no page that exists, or most sentences of its answer used numbers, names or words that are not in the pages it was given and cited (the check on the answer), so the answer was refused rather than guessed. With sb ask --json the second case still lists the sentences in unsupported. A small model does this more often; check_answers = false under [run] shows what the model said without the check. Try the words your notes use; turn on rewrite_questions or search by meaning; measure with sb eval. A different model in [llm.ask] can help (qwen3:4b cites the right page more often than llama3.2, but is about ten times slower).
Embeddings are unavailable¶
warning: embeddings unavailable (...); searching by keyword only
[llm.embed] is set but Ollama cannot be reached or the model is not pulled (ollama pull nomic-embed-text). Search falls back to keywords; nothing breaks.
Ticks are not registered¶
Run sb index after ticking. The tick must be - [x] [[Source title]] on the line of the source, in a daily note under wiki/daily/. To undo a tick, untick the box in the note of the day the source was processed and run sb index: the source goes back to processed. A box unticked in any other day's note changes nothing. See Read your notes.
The nightly job¶
The job stays running and never finishes¶
The log shows getcwd: Operation not permitted, or nothing at all. macOS privacy is waiting for, or has denied, access to ~/Documents. Click Allow on the dialog, or allow the Python that runs sb under System Settings, Privacy & Security, Files & Folders (realpath "$(head -1 "$(command -v sb)" | cut -c3-)" prints which one: First run). Then:
launchctl kickstart -k gui/$(id -u)/com.esbi-cli.nightly
An agent cannot click that dialog for you. See Run it every night.
sb doctor complains about the job¶
| Line | Meaning and fix |
|---|---|
WARN nightly job: not installed |
sb schedule install |
WARN nightly job: installed for 03:00 but config.toml says 04:30 |
Install again for the new time: sb schedule install |
WARN platform: esbi-cli is only supported on macOS for now (...) |
Not macOS. Nothing is promised there; it may work. See Other systems |
WARN nightly job: the nightly job uses launchd, which only macOS has |
Not macOS: add the line that sb schedule status prints to cron |
error: the nightly job uses launchd, which only macOS has |
You ran sb schedule install or uninstall where there is no launchd (not macOS, so not a supported system). sb schedule status prints the same advice but exits 0. The message includes the cron line to add with crontab -e; see cron |
WARN last run: no scheduled run yet |
The job has not run since you installed it |
The cron job never runs, and its log says sb: not found |
An old cron line that starts with a bare sb: cron's PATH is /usr/bin:/bin. Use the line sb schedule status prints now, which names sb by its absolute path |
| The hourly check stops running batches before the queue is empty | [run].max_batches_per_day (default 6) scheduled batches a day were reached, the nightly one included, or the latest scheduled batch stopped for a model outage or an interruption. The rest waits for the next night: raise max_batches_per_day under [run], or run sb run by hand |
WARN last run: DATE: the model server was unreachable (...) |
The latest run (manual or scheduled) stopped because the model was down. Nothing was lost. Start Ollama (brew services start ollama, or open the app) or fix the model line above it, then sb run. The hourly check retries by itself, and a run that reaches the model clears the warning. The daily index says the same, first under To review |
error: no .../bin/sb (run uv sync if this is a checkout) |
schedule install could not find sb in the running environment: reinstall it |
Nothing happened overnight? Read <vault>/.esbi/logs/nightly.log. Not due: the nightly run already happened. is normal for the hourly checks when nothing is queued.
Mail¶
Messages from email capture, with fixes, are in Capture email. Which mail is taken and why a mail labelled late is still found: Which mail is taken. No keyring on a server or in WSL: Email where there is no keyring.
Git and backup¶
| Message | Meaning and fix |
|---|---|
warning: git commit failed: ... |
A commit failed (a git identity not set, a lock). The notes are written; fix git (git config user.name, user.email) and the next run commits |
warning: vault backup not pushed: ... |
The remote cannot be reached or refused the push. The run is fine; fix the remote (git -C VAULT remote -v) |
error: reingest rewrites every note and relies on the vault's git history to undo it; install git and run sb init first. |
sb reingest needs a git history: install git and run sb init |
History: git is not installed, so the vault has no version history. |
Optional. Install git and run sb init again |
error: ... is not an export of this wiki and has other files: not touching it |
sb export --out pointed at a folder that is not an export; pick an empty one |
Files, disks and unusual setups¶
These can happen on any system. Several show up most often where esbi-cli is not supported (a fresh Linux user, a container, WSL); they are listed because they were found and fixed there, not because those systems are supported.
| Message or symptom | Meaning and fix |
|---|---|
error: Permission denied: /path/to/file |
A file or folder could not be written: the vault is read-only, the disk is full, or the user has no permission. One line and exit 1, no traceback. With --json: {"error": ..., "code": "os_error"}. Fix the permission or free space, then run it again; nothing was lost |
error: the vault's state database cannot be used (...). Is the vault folder read-only, full or in use by another program? |
The queue or index database under .esbi/ cannot be opened or written. The same causes (read-only or full vault), or another program using it. It has the code os_error too |
error: this user has no home folder ($HOME is not set): give explicit paths (--vault, --config-file, --config) |
A container or service user with no home. sb version still starts, and a command that needs a home says so; give it the paths explicitly |
sb run \| head stops after a few lines |
Safe. The closed pipe stops the run quietly and the source being read goes back in the queue with no attempt counted, so it is never parked for it. Run it again |
A source with a very long title (Japanese, Chinese, Korean, emoji) was parked as failed with File name too long |
A page title is cut to 240 bytes (never inside a character), because some file systems (ext4, common on Linux) limit a file name to 255 bytes, not characters. A source parked for this goes back with sb retry |
A source titled foo became foo (2) because a page Foo exists |
On purpose. A page or file name that differs from a taken one only by case, or only by Unicode form (é as one character or as e plus an accent), counts as taken, so a vault shared between a Mac and Linux never holds two names that a Mac sees as one. A re-read of the same address still updates its page |
Updated to X.Y.Z. Run `sb version` to confirm. after sb update |
The upgrade changed the installed version, and the new one is named |
sb update prints Nothing changed: X.Y.Z is still installed. The command found nothing to upgrade (an exact version pin, or the new release is not on the package index yet: try again in a few minutes). |
The upgrade command exited 0 but changed nothing: a pinned install, or a release the package index does not list yet. Wait a few minutes and run sb update again, or reinstall by hand (Update esbi-cli) |
sb today prints the link but nothing opens |
The link is opened with open on macOS (xdg-open elsewhere, an unsupported system); otherwise open the link yourself. With [notes].viewer = "none" it prints the path |
sb export wrote nowhere you expected |
Without --out it writes to the site folder of the vault |
There is no keyring on this system to keep the password in (...) |
Email capture needs a desktop keyring: Email where there is no keyring |