# Troubleshooting

> Doctor output, Homebrew SQLite on macOS, model pulls, stale embeddings, switching backend or Bun version, resident background issues, and Windows llama.cpp startup.

Section: Guides  
Canonical: https://gno.sh/docs/troubleshooting  
Markdown: https://gno.sh/docs/troubleshooting.md

## First stop: gno doctor

```
gno doctor
```

Start here: `gno doctor` runs the health checks GNO ships, including `embedding-fingerprint` (current fingerprint, pending/stale chunks, mixed stored groups) and `collection-roots` (collection folders that are missing or unreadable right now). Read its output carefully before anything else.

For embedding freshness, look for the `embedding-fingerprint` check. It reports the current fingerprint, pending/stale chunks, legacy empty-fingerprint vectors, and mixed stored fingerprint groups.

## A slow or stuck command

Set `GNO_PHASE_TRACE=1` to see where a slow or stuck CLI command spends its time. Each startup phase (write lease, index open, migrations, collection walk, each file, the file processor) prints one line to stderr with the milliseconds since the process started; stdout is unchanged, so `--json` output stays valid.

```
GNO_PHASE_TRACE=1 gno update
# gno-phase 461 4856 sync: file a.md
# gno-phase 473 4856 sync: record adapter selected
```

The last line a stuck command printed names the phase it is in. Include those lines when you report a hang.

## Slow first request or a model failure

The first model request, and the first one after five minutes without model use, has to load the model again, so it is slower. Status polling does not keep models loaded. Check the [model timeout settings](https://gno.sh/docs/configuration#model-timeouts) and compare the same query, model, collection, and options before and after the idle period. Query expansion has its own time limit on a cold start and falls back to a search without expansion when it runs out; raising model timeouts does not extend that limit.

If a model fails to start, exits, or returns an invalid response, GNO reports a structured error and does not retry the request for you. Save the full error, stderr, model URIs, and GNO version, then run the request again. In hybrid results, `meta.vectorsUsed: false` means no vector search succeeded, and a fully lexical fallback shows `meta.mode: "bm25_only"`. A vector search that ran and found nothing returns `vectorsUsed: true` with no hits, so a failed model load never looks like an empty semantic result.

If you see `Unsupported node-llama-cpp simulator source`, reinstall GNO so it uses the node-llama-cpp version it ships with, and do not patch installed native files. When you measure memory, include the separate model process; the main GNO process RSS does not include model allocations.

## Rare or missing filtered results

Filters apply before GNO limits the candidate list, so a rare match is not pushed out by more common ones. Check collection/path, tags, dates, category, author, exclusions, and memory visibility before raising limits. Vector and hybrid language filters match the language of each chunk; a language filter on lexical-only search is not supported yet. Thresholds, deduplication, or limited coverage can leave fewer than K results. Use `gno query "your query" --explain` to see fallback and ranking details.

## Collection root could not be resolved

`gno update` or the watcher printed `[SOURCE_AVAILABILITY_UNKNOWN] Collection root could not be resolved (...)` for a collection. Its folder was missing when the sync ran: an unmounted drive, an offline Google Drive or other sync folder, or a network share that was down. GNO kept the collection's documents searchable and changed nothing.

While the folder is away, `gno status` names the collection, its folder, and the error (`rootError` in `--json`), `gno doctor` reports a `collection-roots` warning and is not healthy, and the Web UI shows a **Folder access** item in the Health Center and a note on the collection's card. These checks run each time you look, so they clear as soon as the folder is back.

Bring the folder back (mount the drive, start the sync client) and run `gno update` again to pick up changes made meanwhile. If the folder moved for good, change the collection's `path` in the config file and run `gno update`, or remove the collection with `gno collection remove`.

## “This index holds collections the config does not list”

A command opened an index that contains documents of collections its config does not list, and refused so nothing is deleted. This happens when you remove a collection by editing the config file, or when a different config (`--config other.yml`, or an SDK client given a config but no `dbPath`) points at your normal index. `gno status` lists them under `Collections not in this config`. A running `gno serve`, `gno daemon`, `gno mcp`, or SDK client refuses requests the same way (REST: `409 STALE_COLLECTIONS`).

- The config is wrong (a test or second config): use a separate index with `--index <name>` (SDK: pass `dbPath`), or run with your usual config.
- You meant to remove the collection: run `gno cleanup` with your usual config; it names each collection it deletes. `gno collection remove <name>` does this in one step next time.
- You want it back: add the collection to the config again.

## Cloud placeholders skipped under sourceAvailability local

Opt-in `sourceAvailability: local` (collection config) is separate from `egressPolicy`. It skips any file that the cloud provider would have to download first. Receipts may show `CLOUD_PLACEHOLDER`, `CLOUD_PARTIAL`, `DATALESS_DIRECTORY`, or a `SOURCE_AVAILABILITY_*` code when GNO cannot confirm a file is local. Files you already indexed under a folder GNO cannot check stay in the index. Local mode works on macOS with Google Drive (My Drive and individual Shared drives), iCloud Drive, and OneDrive, including SharePoint libraries synced at the top level of OneDrive. If you get `SOURCE_AVAILABILITY_UNSUPPORTED` for a collection at the Google `Shared drives` folder itself, create one collection per Shared drive instead. Cloud filesystems on Windows and Linux are not supported. In this mode GNO never pins, evicts, or downloads files. To index a skipped file, make it available offline in the provider first. A collection that reports `SOURCE_AVAILABILITY_UNSUPPORTED` on every sync is retried less and less often by a running server or daemon, but it never indexes in that state: remove `sourceAvailability: local` from it, or set it to `any`. See [Configuration → Source availability](https://gno.sh/docs/configuration#source-availability).

## Retrieval activation is blocked or degraded

Check `gno doctor`, `gno status --json`, or the dashboard Health Center. A ready collection has completed a bounded, corpus-derived lexical search and returned its expected document. Semantic search may still be pending independently while models or embeddings are prepared.

- `no_documents` — the collection has nothing indexed yet. This is informational, not a failure: doctor marks it with `i` and does not exit non-zero for it. Add supported files to the folder, then run `gno update`.
- `no_probe_term` — add a supported text document or fix the collection filters, then run `gno index <collection> --no-embed`.
- `index_out_of_sync` or `retrieval_mismatch` — rebuild that collection’s lexical index, then rerun doctor.
- Connector failures — open Web **Connectors** and run its explicit read-only verification action again after repairing the configured MCP command. Skill installations remain runtime unverifiable; that is not a passed connector proof.
- A long connector list is cut to 64 rows, but the checks left out still count: status summarizes them, and a failed or pending one keeps status at warning even when it is not listed.
- Wrong workspace in a connector — reinstall it from the intended GNO workspace. The installer pins the active index and absolute config, data, and cache roots; passive status does not rewrite a stale client entry.

Do not edit GNO’s search tables directly. Changes made outside GNO may not be detected. If you suspect them, rebuild the collection with `gno index <collection> --no-embed`.

## `Error: database is locked` or exit code 4

GNO writers wait their turn on one shared write lease, so CLI and MCP writes that overlap do not fail with `Error: database is locked`.

Exit code 4 means another writer held the lease past `--lock-wait` (default 120s). This is contention, not corruption — retry the command. Raise `--lock-wait` for long reindexes, or pass `--no-wait` in scripts that prefer failing fast. Reads do not take the write lease, but resident reader admission still has bounded capacity. A full reader queue returns HTTP 429; shutdown or unavailable admission returns HTTP 503.

`chunks deferred by index contention` means rerun `gno embed` after the other writer finishes. If you run a GNO version older than 1.38, upgrade.

A running `gno serve` or `gno daemon` uses the same lease for file watching and background embedding, and pauses that work while a CLI writer holds it. A CLI command next to a busy resident may wait and, at worst, exit 4. A raw `database is locked` error means a writer outside the lease: an older GNO version or another tool with the index open for writing.

## Resident uses CPU, embeddings stay pending, or `--stop` needs SIGKILL

Run `gno status` (or `gno serve --status` / `gno daemon --status`) and look for `Background issues`. Each detached resident gets one status request with a 500ms budget, so status never waits on a hung process.

- `background embed failing` or `parked`: background embed passes keep failing. Retries back off (30s, 60s, 120s, 240s) and stop after 5 failed passes in a row; the chunks stay pending. The serve or daemon log has one line per retry step with the error. Run `gno embed` in the foreground to see per-chunk errors.
- `background embed pass running for ...`: one pass has run for more than 15 minutes.
- `resident did not answer its status request`: the process is alive but busy or stopped. `--stop` still sends SIGTERM first and waits up to 12 seconds. A SIGKILL report means the resident stayed unresponsive for that whole time.

`gno status --json` carries the same data as `backgroundIssues` (absent when nothing is wrong). The serve and daemon logs are append-only across runs, so repeated `GNO server running` / `Shutting down...` blocks are separate starts and stops, not a restart loop.

Captures and document saves (Web UI and `PUT /api/docs/:id`) also take the write lease. One that cannot get it in time returns the busy error (`409 LOCKED` for REST saves) and writes nothing; try again once the other writer finishes.

## A server or MCP client keeps running an old Bun

A running `gno serve` or `gno daemon` keeps the Bun it started with, and a client registration keeps the Bun path written when it was installed. `gno status` lists both under `Bun runtime`, and `gno doctor` reports them as `resident-runtime` and `mcp-runtime`, each with its fix:

- Registration on an old or missing Bun: run the printed `gno mcp install --target <client> --force`. It rewrites only the Bun and GNO paths and keeps the rest of the entry.
- Server on an old Bun: restart it with `gno serve --stop` and `gno serve --detach` (or the same for `gno daemon`). A server started by an older GNO version is left alone by `--stop`; stop it with the printed `kill <pid>`, then start it again.

GNO needs Bun 1.4.1 or later. While indexing, a message GNO cannot read back fails only that file with `PROCESSOR_MESSAGE_FAILED` and one log line naming it; indexing continues with the next file.

## A write timed out or a retry reports a request error

Check the request ID first with `gno request-status <id>` (or `GET /api/requests/:requestId`). Committed: do not resend. Pending or not found: repeat the identical call with the same ID.

- `REQUEST_ID_CONFLICT`: use a new ID for the changed payload.
- `REQUEST_PENDING`: retry the same ID later.
- `REQUEST_RECOVERY_CONFLICT`: read the current document or recall the current fact and decide again.
- `REQUEST_EXPIRED`: check current state before sending a new ID.
- `REQUEST_CAPACITY_EXHAUSTED`: send new writes without a request ID; existing IDs still replay.
- `REQUEST_LEDGER_UNAVAILABLE`: make sure the data directory’s `write-receipts` folder is readable and writable by your user.
- `CAPTURE_SYNC_FAILED` or `MEMORY_SYNC_FAILED` with a request ID: retry the same ID, or run `gno update`.

Codes and semantics: [Retry-safe writes](https://gno.sh/docs/retry-safe-writes#errors).

## Vector search fails on macOS

Symptom: `gno vsearch` or hybrid queries fail with a SQLite extension loading error.

Cause: the stock Apple SQLite doesn’t support loading extensions. GNO needs the Homebrew build.

```
brew install sqlite3
gno doctor
```

## Models fail to download

Symptom: the first `gno query` or `gno ask` stalls during a model pull.

Solutions:

- Run `gno models pull` explicitly to see real-time progress and errors.
- HTTP rerank endpoints are external services, so model pulls skip them instead of treating them as downloadable GGUF files.
- If you’re offline or behind a firewall, set `GNO_NO_AUTO_DOWNLOAD=1` and download model files by hand into the cache.
- Clear a stuck download with `gno models clean <preset>`.
- If a cached download is reported as non-GGUF or intercepted HTML, fix network access and rerun `gno models pull --force`.

## Results feel stale after editing

Re-scan the index:

```
gno update
```

Or run `gno daemon` in the background for continuous indexing.

A deleted file you restore unchanged comes back on the next sync with its existing embeddings and one `reactivate` event in the change journal. A changed title or text, or a different embedding model, needs new embeddings. Run `gno doctor` and a normal `gno embed` before reaching for `gno embed --force`. If doctor reports a missing or corrupt vector index, follow its repair step; GNO does not fall back to older vectors. Interrupted embedding runs resume where they stopped.

## Update reports an invalid PDF

The PDF is incomplete or damaged. GNO isolates it as a non-fatal `CORRUPT` document, continues indexing the collection, and keeps the Web UI available. It does not retry the unchanged file on every update.

```
qpdf --check /path/to/file.pdf
```

Replace or re-export the PDF, then run `gno update` or **Update All**. A changed source hash makes GNO try the repaired file again.

## A large spreadsheet never finishes, or is stopped with TIMEOUT or MEMORY\_LIMIT

GNO 2.8.2 could spend hours and tens of gigabytes on one large Excel workbook (for example a pivot report with a sheet of tens of thousands of rows) and write nothing to the index; 2.8.1 also used tens of gigabytes on such a file. Converting a sheet went through an HTML table whose cost grew with the square of its rows and used about 20 KB per cell, and 2.8.2 added Markdown code detection over the converted table, which grew with the square of its cells. Current versions build each sheet's table directly from the sheet data, never run code detection on converted files, and parse very large Markdown tables without the table rules, so such a workbook indexes in seconds. A Markdown note that is mostly one such table also indexes in time proportional to its size: a note of 23 columns by 40,000 rows takes a few seconds, including rows with an unmatched `]`, an unclosed `[[` or an unclosed `<!--`, which used to stop it at the per-file budget.

Each file is also indexed under a per-file budget covering conversion and the processing after it, and is stopped mid-step when it runs over. A file still converting after 10 seconds is named on stderr by `gno update` and `gno index`. A file that runs past its time budget, or pushes GNO's memory past the memory budget, is stopped and recorded as `TIMEOUT` or `MEMORY_LIMIT`; the rest of the collection still indexes. `gno update` output, `gno status` and `gno audit` name the file, and the next update tries it again.

To let a legitimately large file finish, raise the [conversion budget](https://gno.sh/docs/configuration#conversion-budget):

```
conversion:
  timeoutMs: 300000
  maxMemoryMb: 12288
```

To skip it instead, add it to the collection's `exclude` list.

## Results feel off after switching models

Vector-based search uses the embedding model that was active when the index was built. Doctor shows this through the `embedding-fingerprint` check. After switching embeddings, re-embed:

```
gno doctor
gno embed # re-embed stale/pending chunks
gno embed notes # or one collection
```

`gno embed` retries transient embedding failures within the same run. If doctor still reports stale, legacy, or mixed vectors after a normal embed, force a full refresh:

```
gno embed --force
```

`gno embed --force` uses the same same-run retry path. If a run still fails, rerun with verbose output so GNO prints sample failures and the retry hint:

```
gno --verbose embed --force
```

## Check which GPU backend is detected

Before forcing a backend, see what `node-llama-cpp` actually detects. This works on Linux, Windows, and macOS and reports the active GPU backend (CUDA, Vulkan, or Metal), available VRAM, and which prebuilt binary is in use:

```
bunx --bun node-llama-cpp inspect gpu
```

(`npx --no node-llama-cpp inspect gpu` works too.) If CUDA or Vulkan shows as available here but GNO still runs on CPU, the backend is being selected or initialized incorrectly — see below. If it is not available here, the GPU driver or toolchain is the problem, not GNO.

## Switching backend or Bun version

Vector identity is the embedding model weights, formatter, dimensions, context size and truncation policy. Changing `GNO_LLAMA_GPU` / `NODE_LLAMA_CPP_GPU`, the Bun version (for example a scheduled CLI and the desktop app on different Bun releases), the `node-llama-cpp` version or the CPU thread count does not start a new partition. Those details are recorded as provenance.

The first time a runtime meets an existing partition, GNO re-embeds up to 8 stored chunks and compares them with the stored vectors. Every sampled chunk must reach cosine 0.99. With the default model, Bun version, thread count, and GPU or CPU switches pass this check, so they resume the backlog in the same partition. GNO remembers the result for each partition and runtime.

```
GNO_LLAMA_GPU=false gno embed --yes # resumes the backlog
```

A runtime below the threshold is incompatible. Its queries use lexical retrieval only and carry a `vector_runtime_incompatible` warning, status lists it under the partition, and `gno embed` states that it would build a separate partition, the full chunk count and an estimate, then asks for confirmation. Non-interactive runs need `gno embed --new-partition`; `--yes` alone never confirms.

Later versions of that runtime reuse the confirmed partition: GNO measures every partition of the same vector space and picks the first compatible one. The same confirmation applies when a vector-defining setting (for example `GNO_EMBED_CONTEXT_SIZE`) changes for a model that already has vectors. Status counts the runtime-independent partition and lists which runtimes read each partition.

After you upgrade, GNO converts an existing index once, without re-embedding. The most complete partition that passes the check becomes the runtime-independent one and the others are marked `shadow`. If two are equally complete, GNO converts nothing and status and embed report the tie; drop all but one.

```
gno status # lists partitions and their drop commands
gno vec drop 9a8b7c6d5e4f
```

## Windows model startup hangs

Symptom: `gno index`, `gno embed`, or `gno doctor` appears stuck while loading `node-llama-cpp`, often around a Vulkan backend load test.

GNO defaults to prebuilt local-model backends, times out backend initialization, and retries CPU on Windows when automatic GPU backend selection fails. Run diagnostics first:

```
gno doctor
```

To force CPU mode for a constrained Windows machine, run:

```
GNO_LLAMA_GPU=false gno embed --yes
```

If CPU embedding still consumes too much memory, keep the adaptive default or set an explicit small context pool. On CPU-only systems, GNO defaults to one context on low-memory Windows machines and at most two contexts elsewhere:

```
GNO_EMBED_CONTEXTS=1 gno embed --yes
```

Advanced CPU tuning is available when you want to trade throughput, memory, and per-context parallelism:

```
GNO_EMBED_CONTEXTS=2 GNO_EMBED_THREADS=4 gno embed --yes
GNO_EMBED_CONTEXT_SIZE=512 gno embed --yes
```

Only opt into source builds when you intentionally have Visual Studio Build Tools and a working native toolchain:

```
GNO_LLAMA_BUILD=autoAttempt gno doctor
```

## Session archive problems

Binding mismatches, partial or quarantined imports, a busy archive, and sessions missing from search are covered in the [session archive troubleshooting](https://gno.sh/docs/sessions#troubleshooting) section, with every code in its [error-code table](https://gno.sh/docs/sessions#error-codes).

Automation problems (a hook that says accepted but nothing was archived, not running: no daemon, a missing hook entry, retries and backoff) are covered in [Failures, retries, and recovery](https://gno.sh/docs/sessions#failures-retries-and-recovery). The quickest checks:

```
gno --config ~/gno-sessions/archive.yml --index sessions sessions status        # state and recovery action
gno --config ~/gno-sessions/archive.yml --index sessions sessions automation run <profile>
GNO_SESSIONS_HOOKS=off claude                                                  # run Claude Code with the hook disabled
```

## Still stuck?

- Open an issue: [github.com/gmickel/gno/issues](https://github.com/gmickel/gno/issues)
- Include your `gno doctor` output and the exact command you ran.
