Source: https://velofy.co/querion/troubleshooting/

[Querion](https://velofy.co/querion/) / Guides

# Troubleshooting

Fix common Querion problems: config errors, Claude Code CLI failures, refused queries, allowlist refusals, missing charts and the daily cap.

Each entry starts with the message or symptom you see, taken from the code, then the cause and the fix.

## Where problems come from

A question passes through four things, in order: the config file, the Claude Code command-line interface (CLI), the database connection, and the read-only checks. Most problems sit in one of them.

`querion check` tests the first three. It loads the config, looks for the `claude` binary and runs `SELECT 1` on the database. Run it first. It does not test the CLI login, and it exits with code 0 even when a line reports a problem, so read its output.

When a `querion` command cannot run, it usually prints `error: ...` on standard error and exits with code 1. Errors that happen while a question runs are different: Structured Query Language (SQL) errors, refused paths and failed Hypertext Transfer Protocol (HTTP) requests to your application programming interfaces (APIs) go back to the model as the directive’s result, so you see them only if the model mentions them in its answer.

## Config and environment

### `error: Config not found at 'querion.yaml'`

Querion looks for the `-c PATH` flag, then the `QUERION_CONFIG` environment variable, then `querion.yaml` in the current directory. Copy the example, or point at your file. The flag goes before the subcommand:

```
cp querion.example.yaml querion.yaml
querion -c path/to/querion.yaml check
```

### `got an unexpected keyword argument` or `missing 1 required positional argument`

A key inside `database`, `limits`, `trove` or a source block is misspelled or unknown, or a source is missing `name` or `base_url`. Those blocks map to fixed fields, so an unknown key is an error. Unknown keys at the top level are ignored without a warning. The valid keys are in the [configuration reference](https://velofy.co/querion/configuration/).

### A `${VAR}` value is empty or wrong

*   A variable that is not set becomes an empty string, with no warning. An unset `QUERION_DATABASE_DSN` shows up later as `no database DSN configured (database.dsn in querion.yaml)`.
*   Names may contain only uppercase letters, digits and underscores. `${my_key}` is not replaced; it stays in the value as typed.
*   The CLI loads a `.env` file only when `python-dotenv` is installed (the `dotenv` or `all` extra). Without it, export the variables in your shell.
*   Interpolation always produces text. Write numbers and booleans literally in the file. `enabled: ${TROVE_ON}` with `TROVE_ON=false` becomes the text `"false"`, which Python treats as true, and a number such as `row_cap: ${ROWS}` breaks every query.

## The Claude Code CLI

### `querion check` reports the claude CLI as `NOT FOUND`

Querion cannot find the `claude` binary. Install Claude Code and run `claude` once to log in, or set `claude_bin` in `querion.yaml` to the full path.

### `claude CLI not found`

The same cause, reported while a question runs. The full message also says to install Claude Code and run `claude` once to log in, or to set `claude_bin`. The question ends with status `error`, and the answer reads `Something broke while analysing:` followed by this message.

### `claude CLI failed (exit N): ...` or `claude returned an error: ...`

The CLI started but failed. Up to 500 characters of its error output follow the colon. Authentication lives in the home directory of the user that runs Querion, so run `claude` as that same user to check that it is logged in. Also check that `model` in `querion.yaml` is an alias the CLI accepts.

### `claude CLI timed out after 200s`

Each model call has a fixed 200-second limit. The whole knowledge block is sent on every call, so a very large schema makes every call slower. A curated `schema_doc` that lists only the tables that matter keeps it small.

## Database

### `querion check` reports `database` as `ERROR`

The `SELECT 1` test failed. The error text from the Postgres driver follows. The connection timeout is 5 seconds. Check the host, port, database name, user and password in the connection string, and that the host can reach the server.

### `querion check` says `ok (read-only)`, but is the role read-only?

That label is printed whenever `SELECT 1` succeeds. It does not inspect the role’s privileges. Check the role yourself, connected as that role:

```
SHOW default_transaction_read_only;   -- expect: on
```

### The model uses table or column names that do not exist

*   Live introspection reads only the `public` schema.
*   The server builds its knowledge once at startup. Restart `querion serve` after a schema change.
*   If introspection failed at startup, the model received `(schema introspection failed: ...)` instead of a schema. Fix the connection, then restart.
*   For a large or unusual schema, generate a file, edit it, and set `database.schema_doc`:

```
querion introspect -o docs/schema.md
```

### A harmless query keeps being refused

The model receives `SQL error:` and the reason, and it can rewrite the query in the next turn. When it keeps failing, the reason is one of these:

*   `query contains a forbidden (write/DDL) keyword`: a denylisted word appears as a whole word somewhere in the query, including inside a string or as a column name (`comment`, `set`, `do`, `analyze` and others). A query cannot name a column called exactly one of those words, so it cannot select, filter or group by it. Expose it through a view that renames the column, grant the view to the read-only role, and describe the view in `schema_doc`.
*   `only SELECT/WITH queries are allowed (got '...')`: the first word is not `select` or `with`. A leading comment, an opening parenthesis, `EXPLAIN`, or no space after the keyword (`select*from`) all cause this.
*   `multiple statements are not allowed`: a semicolon appears inside the query, even inside a string literal.

The full list of denylisted words is in the [safety model](https://velofy.co/querion/safety-model/).

### Queries are cancelled for taking too long

Querion sets `statement_timeout` to `database.statement_timeout_ms` (default 15000) on every connection, and Postgres cancels any query that runs longer. Raise the value in `querion.yaml`, or index the columns your questions filter on. Querion’s setting replaces any `statement_timeout` default on the role for its own sessions.

## HTTP sources

### `Refused: '/path' is not on the 'NAME' safe-read allowlist...`

The source has `safe_get` set and the path matches none of its patterns. If the endpoint is a safe read, add a pattern for it. Patterns match from the start of the path, ignore case, and ignore the query string. See [Connecting data sources](https://velofy.co/querion/data-sources/).

### `unknown source 'NAME'. Configured: ...`

The model named a source that is not in the registry. A common cause is the source name itself. Querion lowercases the name it reads from a `source:NAME` block before looking it up, and it reads only letters, digits, `_` and `-`. A source configured as `Stripe` or `my.api` can never be reached. Use names such as `stripe` or `billing-api`.

### The API answers `HTTP 401` or `HTTP 403`

A response with an error status is not raised as an error. It goes back to the model as `HTTP <status>` followed by the body. Check that the secret is set in the environment, and that `auth_header` has the form `"Header-Name: value"`. Without a colon, no header is sent at all, and nothing warns you.

### `NAME GET failed: ...`

The request was refused or raised an error. The text after the colon says which: a path refused by `safe_get`, a write verb such as `POST`, a network failure, or a timeout. The per-source timeout is `timeout` (default 25 seconds).

### The answer seems to use only part of a response

Only the first 6000 characters of each response reach the model. Describe filters and page-size parameters in the source’s docs file so the model asks for less.

## Answers and charts

### The answer is `(no answer)`

The final reply was empty once stray directive blocks were removed. Ask again, or rephrase the question.

### The answer starts with `Something broke while analysing:`

An error stopped the loop. The message after the colon names it. CLI failures and timeouts are the usual source; see above.

### The answer stops early or looks partial

The question reached `limits.max_steps` (default 7) and Querion asked for a final answer. Raise `max_steps`, or split the question.

### Charts do not show in the terminal

`querion ask` prints only `[N chart(s) generated]`. Each image is written to the system temporary directory as `querion_chart_*.png` and is not deleted. The model is told the file path and may include it in the answer. Without matplotlib, the model receives a text bar chart of the first series instead, which it can include in its answer.

### The SQL in a terminal answer is not exactly what ran

In the terminal, the SQL you see is the plain `sql` block the model writes for display. The code does not compare it with what ran. In the web user interface (UI), “Show SQL” lists the exact statements that ran successfully, collected by the runner.

## Web server

### `No module named 'uvicorn'` or `fastapi and uvicorn are required`

`querion serve` needs the web packages. Install the `web` or `all` extra.

### `Daily limit reached. Resets in ... (00:00 UTC).` (HTTP 429)

The caller used up `limits.daily_per_user` questions for the day. Raise the limit, or set it to `0` for no limit. Counts are kept in memory and saved to `.querion_usage.json` next to the config file, so a restart keeps them.

Behind a reverse proxy, Querion may see every request as coming from the proxy’s address, so all users share one counter. Have the proxy set the `X-Querion-User` header to the signed-in user, replacing any value the client sent.

### The page loads but is blank, or charts are missing

The page loads Alpine.js and Chart.js from cdn.jsdelivr.net. The browser needs access to that host.

### Polling returns `{"error": "unknown run"}` (HTTP 404)

Runs live in memory. The server restarted, or the run was more than an hour old and was removed when a later question was accepted.

### Several questions at once slow the host

Each accepted question starts its own thread, and each model call starts its own `claude` process. There is no queue. The daily cap limits how many questions each caller asks per day, not how many run at once.

## Limits you will hit

These values are fixed in the code unless a key is named:

*   Rows shown to the model: the first 30 per query, cells cut at 40 characters. Rows fetched: `database.row_cap` (default 500).
*   API response shown to the model: 6000 characters.
*   One model call: 200 seconds. Postgres connection: 5 seconds.
*   Model calls per question: up to `limits.max_steps + 1`.
*   Directives run per reply: `limits.max_directives_per_step` (default 4). The prompt always tells the model “up to 4”.
*   Introspection: the `public` schema only.
*   Web runs: kept in memory, removed after one hour. One server process only.

The [project status](https://velofy.co/querion/status/) page lists the known limits of the current release.
