Troubleshooting¶
Start with the system's own diagnosis instead of inferring a cause from an empty retrieval result.
doctor checks configuration, domain validation, database connectivity, migrations, corpus state, graph state, and credentials without spending model tokens. Add --probe only when you want one live embedding and generation round trip.
Fast symptom map¶
| Symptom | Most likely check | First action |
|---|---|---|
| Connection refused on port 5433 | Postgres is down or the URL differs | docker compose up -d --wait |
| Relation or table does not exist | Migrations have not run | uv run sci-rag db upgrade |
| No Google credentials configured | The command needs a real model | Configure AI Studio or Vertex, or use offline retrieval |
| Retrieval returns no items | Empty corpus, restrictive scope, or failed stages | sci-rag stats, then inspect stage traces |
community says skipped |
Any rights or metadata filter is active | Expected: stored summaries cannot be safely post-filtered |
| PDF text or tables are mangled | The pypdf fallback handled a difficult file | uv sync --extra docling, then re-ingest changed content |
Ingest says skipped_duplicate |
The normalized content hash already exists | No action unless the source content changed |
| Embedding dimension mismatch | Model/dimension changed after schema creation | Plan a migration and re-embedding pass |
| API returns 401 or 403 | Missing key, unknown key, or missing scope | Check SCI_RAG_API_KEYS and the stable error code |
| One retrieval layer timed out | That stage exceeded the profile timeout | Read traces and degraded_stages; the request intentionally survived |
| Crossref enrichment reports a failure | Network, rate limit, or malformed work metadata | Retry the bounded set; failed documents keep their prior metadata |
Postgres is unreachable¶
Confirm the container and port before changing configuration:
The included compose service maps PostgreSQL to host port 5433. If you use another server, set the full async SQLAlchemy URL in SCI_RAG_DATABASE_URL and ensure the pgvector extension is available.
Expected output
If the container is healthy but doctor cannot connect, compare .env with docker-compose.yml; a copied port 5432 is a common mismatch.
A command needs model credentials¶
Three modes are supported:
SCI_RAG_GOOGLE_API_KEY: Google AI Studio.SCI_RAG_GCP_PROJECTplus Application Default Credentials: Vertex AI.SCI_RAG_EMBEDDING_PROVIDER=local-hash: offline, deterministic retrieval mechanics only.
The offline embedder is lexical, not semantic. It does not make graph extraction, HyDE, community summarization, answer generation, reranking through the LLM adapter, or judged-answer evaluation available. A refusal at that boundary is correct behavior.
Use uv run sci-rag doctor --probe after configuring a credential. The probe spends one small request and distinguishes a present-looking credential from one the provider accepts.
Running offline on purpose¶
A project that sets SCI_RAG_EMBEDDING_PROVIDER=local-hash, configures no credential for any provider, and leaves SCI_RAG_LLM_MODEL and its siblings at their shipped defaults is a supported mode, not a half-finished setup. The setup wizard writes exactly that for credentials: offline. doctor reports the unavailable generation features as warnings and exits 0, so a green run means the offline pipeline is healthy rather than that a model is reachable.
Reaching for a model and missing the credential is still a failure. doctor reports FAIL when the Google embedding provider is selected without credentials, and when a generation model is named explicitly, such as SCI_RAG_LLM_MODEL=anthropic:claude-opus-5, with no key or project behind it. Writing a model id is how you say you intend to generate, so the diagnosis follows what the configuration asks for rather than what happens to be missing.
Retrieval is empty or unexpectedly narrow¶
Check in this order:
uv run sci-rag statsconfirms documents and chunks exist.- The result
tracesshow which stages weresuccess,empty,skipped,timeout, orerror. - Remove filters one family at a time: year, author/journal/DOI, source, then license.
- Confirm that an explicit empty
license_classeslist was not sent. Empty means deny all by design. - Use
--profile interactiveto isolate vector and keyword retrieval from model-dependent layers.
Rights and metadata filters run inside each eligible layer before ranking. They can legitimately turn a broad question into no result. The community stage always becomes skipped for a scoped request because each stored community summary combines multiple documents before request-time scope exists.
Do not repair an empty result by widening rights silently
If the requested license scope contains no supporting document, return no evidence or ask for an explicitly wider scope. Treating unknown as public would be a rights failure, not a recall improvement.
A PDF parses badly¶
The base install uses pypdf as a lightweight fallback. Docling adds table structure and stronger layout parsing, but brings a larger machine-learning stack:
An identical source will still deduplicate. Remove the previously ingested document with the corpus lifecycle commands before replacing its stored parse; the operations guide explains snapshots and backups to take first.
Embeddings no longer match¶
Every chunk carries an embedding version, and the database column has a fixed dimension. Use the planner before acting:
Same-dimension model changes can be re-embedded in place. Cross-dimension changes require a schema migration because pgvector enforces the column width. Do not mix vector dimensions or change SCI_RAG_EMBEDDING_DIM on a populated database and hope the provider adapts.
The server rejects a request¶
REST errors use application/problem+json with a stable code and X-Request-ID. Branch on code, not the English detail. Common authorization codes are missing_key, invalid_key, and insufficient_scope; throttling is rate_limited and includes Retry-After.
With no SCI_RAG_API_KEYS, the server deliberately runs open and logs a warning. That mode is suitable for localhost only. See the API authentication contract before exposing a service.
Still stuck¶
Capture these without secrets:
$ uv run sci-rag doctor
$ uv run sci-rag stats
$ uv run sci-rag retrieve "a representative question" --profile interactive --limit 3
Include the doctor table, stage traces, package version, operating system, and whether Postgres is compose-managed or external. Never paste .env, bearer keys, database passwords, or model credentials into an issue.