Skip to content

Troubleshooting

Start with the system's own diagnosis instead of inferring a cause from an empty retrieval result.

Repository root
$ uv run sci-rag doctor

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:

$ docker compose ps
$ docker compose up -d --wait
$ uv run sci-rag db upgrade

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

Database schema is up to date.

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_PROJECT plus 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:

  1. uv run sci-rag stats confirms documents and chunks exist.
  2. The result traces show which stages were success, empty, skipped, timeout, or error.
  3. Remove filters one family at a time: year, author/journal/DOI, source, then license.
  4. Confirm that an explicit empty license_classes list was not sent. Empty means deny all by design.
  5. Use --profile interactive to 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:

$ uv sync --extra docling
$ uv run sci-rag ingest --manifest path/to/manifest.jsonl

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:

$ uv run sci-rag embed reindex --dry-run

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.