# Troubleshooting This guide covers common issues when working with ADI Starter Edition and how to resolve them. ## Connection issues ### MCP tools do not appear in your agent **Symptoms:** No `adi.*` tools are listed. The agent has no access to ADI. **Check these in order:** 1. **The endpoint and connection settings** match what you were issued on acceptance, and you are signed in to your Zitadel account with your access approved. 2. **The file location** for your MCP config is in the root of the folder you have open in your client, not a subfolder or parent directory. 3. **The trust prompt** in Claude Code requires you to approve a new MCP server when it first appears. If you missed it, close and reopen the project folder. **If tools still do not appear:** - Fully close and reopen your agent (not just the file) - Confirm the endpoint is reachable from your network - Discover capabilities with `adi.get_capabilities` once connected, to confirm which tools are enabled at your trust tier > [!NOTE] > **Connection and authentication.** The server is reached over streamable HTTP at the endpoint provided to you on acceptance. Authentication is required: you sign in with, or create, a Zitadel account on first connection. There is no API key. If your access is still being approved, the tools report a pending status until it is active. ### Python SSL certificate failure **Symptoms:** Python's `urllib` raises `SSL: CERTIFICATE_VERIFY_FAILED`. **Cause:** Some Python builds do not include the necessary intermediate certificates. **Workaround:** Use the `requests` library (which uses `certifi`) or `curl` instead of Python's built-in `urllib`. ## Query issues ### Queries return zero results **Symptoms:** A valid request with a known metric returns 0 rows. **Most common cause:** The time window falls outside the range of your data. For example, if your dataset starts in a given year, a request filtered to an earlier year returns zero results legitimately. **Solutions:** 1. Use relative time windows ("last quarter", "this year", "last 30 days") rather than fixed dates 2. Remove the time filter to confirm data exists, then add it back with the correct range 3. Confirm the metric name is in the contract ### Rate limiting causes empty responses **Symptoms:** First requests work, then subsequent ones return empty or null results, with no rate-limit error shown. **Cause:** Rapid sequential calls can exceed the server's rate limit, and the server may return an empty body rather than a standard error. **Solutions:** 1. Add a short delay between sequential calls 2. Wait a second or two before retrying a failed call 3. Run analytics in logical groups with pauses between them ## Refusal handling ### A request is refused with an unexpected code **Symptoms:** A request is correctly refused, but the code does not match what you expected. **Common refusal codes:** | Code | Meaning | | ----------------------- | --------------------------------------------------------------------------------------- | | `metric_not_found` | The requested metric is not in the contract | | `dimension_not_allowed` | The group-by is not in the metric's allowed list | | `dimension_disallowed` | The group-by is explicitly blocked (for example an email column on the disallowed list) | | `policy_denied` | A policy restriction blocks the column (PII or governance) | | `join_path_absent` | The metrics cannot be safely joined (for example metrics on different base entities) | | `request_malformed` | The request shape or values are invalid | | `contract_not_cached` | The `contract_hash` is unknown to the server; re-send the documents | The full set is on the [Features page](https://docs.agentic-data.com/docs/starter-edition/features). A PII group-by refuses as `dimension_disallowed` when the column is on the metric's disallowed list, or as `policy_denied` when it is an allowed group-by blocked by a policy action. Each refusal carries recovery information in its `details` (such as the available metrics or the allowed group-bys); use it to adjust the request. ### A contract fails validation **Symptoms:** `adi.validate` reports errors on a contract that looks correct. **Cause:** Small schema mismatches are easy to miss on a manual read, and some are subtle enough to silently drop a join or a set of rows. **Action:** Read the path-pointed errors `adi.validate` returns and fix each at the field it points to, rather than working around them. Re-validate until the contract is clean before relying on it. > [!NOTE] > **DuckDB dialect.** When authoring a contract for DuckDB data, declare the contract's SQL dialect as `postgres` (per ADI's engine-to-dialect mapping). There is no `duckdb` SQL dialect, so declaring one will fail validation. ## Running the generated SQL ### A validated query fails when you run it **Symptoms:** `adi.generate_query` returned validated SQL, but the query fails when you run it in your own environment. **Cause:** At t1 you run the SQL yourself. A clean validation confirms the SQL was compiled from the contract; it does not guarantee the query runs, since an underlying column type can still cause a failure at execution. **Action:** Check the underlying column types involved. Where a figure cannot be computed within the contract, compose it outside and flag it. ### Generated SQL is rejected by Snowflake **Symptoms:** The generated SQL runs on Postgres or DuckDB, but Snowflake raises `invalid identifier`, or a boolean predicate does not match. **Cause:** The compiler emits SQL shaped for the `engine` and `sql_dialect` declared in the contract's `datasource` block, not for whichever warehouse the SQL is actually run against. ADI's `engine_dialect_map` maps `duckdb` to the `postgres` dialect, so a contract declaring `engine: duckdb` (or `engine: postgres`) emits Postgres-shaped SQL: lower-case double-quoted identifiers and `is_active = 1` for booleans. Postgres and DuckDB fold that correctly; Snowflake does not, so the same SQL fails there. **Action:** Declare `engine: snowflake` and `sql_dialect: snowflake` in the contract's `datasource` block when the target warehouse is Snowflake. With that declared, the compiler emits Snowflake-shaped SQL: upper-case double-quoted identifiers and `= TRUE` for booleans. Do not strip the double quotes from the identifiers: they preserve the exact case Snowflake's storage uses, and removing them would break a genuinely mixed-case column. ### A filter is refused as request_malformed **Symptoms:** A query request with a filter operator such as `IN` is refused as `request_malformed`. **Cause:** Query-request filter operators are lowercase (`in`, `not_in`, `between`, and so on). Uppercase operators are rejected. The contract's own `default_filters` use SQL-style uppercase operators, which is a separate authoring schema, so the two can look inconsistent. **Action:** Use lowercase operators in the query request. ### A reused contract returns contract_not_cached **Symptoms:** A `generate_query` or `validate` call that reuses a `contract_hash` returns `contract_not_cached`, even though the contract validated earlier. **Cause:** The hosted server runs multiple replicas with a per-replica cache, so a hash cached on one replica can miss on another. This is expected. **Action:** Re-send the contract documents inline; you will get the same hash back. The Beta caches contracts for 24 hours. ## Quick reference | Task | Tool | Key detail | | ----------------------------- | ------------------------ | ------------------------------------------------- | | Discover trust tier and tools | `adi.get_capabilities` | Call at the start of every session | | Fetch the contract schema | `adi.get_authoring_spec` | Use before authoring a contract | | Validate a contract | `adi.validate` | Fix the path-pointed errors it returns | | Generate governed SQL | `adi.generate_query` | Ask in business language; never write SQL by hand | | Problem | Most likely cause | First thing to check | | ------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | | Tools not appearing | Connection or sign-in | Endpoint, Zitadel sign-in (access approved), trust prompt; discover with `adi.get_capabilities` | | Empty results | Time window | Check the date range of your data | | Unexpected refusal code | Out-of-contract request | Read `details` for recovery; full code set on the Features page | | Contract fails validation | Schema mismatch | Read the path-pointed errors; check the SQL dialect | | Query fails when you run it | Underlying column type | You run the SQL yourself at t1; check the column types | | SQL rejected by Snowflake | Contract declared for a different dialect | Confirm `datasource.engine`/`sql_dialect` is `snowflake`; do not strip the quotes | | Filter refused as `request_malformed` | Uppercase operator | Use lowercase operators (`in`) in the query request | | `contract_not_cached` on reuse | Per-replica cache miss | Re-send the contract documents; the same hash returns |