release: 0.46.2 — friendlier hint on missing-table errors for remote tables (#219)

## Summary

`agnes query "DESCRIBE unit_economics"` (where `unit_economics` is `query_mode='remote'`) previously returned DuckDB's nearest-name suggestion (`Did you mean "order_economics"`?), sending users down the wrong path. Now appends a friendly hint about remote tables.

Reproduced from a real analyst session — colleague spent ~30s diagnosing what was actually "this is a remote table, not materialized locally".

## Test plan

- [x] New test: `_query_local("DESCRIBE unit_economics", ...)` against an empty local DuckDB triggers the new hint, original DuckDB error still echoed.
- [x] Negative test: a syntax-error query does NOT trigger the hint (regex only matches "Table with name X does not exist").
- [x] `pytest tests/test_cli_query*.py` clean.
<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/keboola/agnes-the-ai-analyst/pull/219" target="_blank">
  <picture>
    <source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
    <img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open in Devin Review">
  </picture>
</a>
<!-- devin-review-badge-end -->
This commit is contained in:
ZdenekSrotyr 2026-05-07 17:24:10 +02:00 committed by GitHub
parent 378ee40459
commit 50d10443d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 71 additions and 1 deletions

View file

@ -10,6 +10,12 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
## [Unreleased]
## [0.46.2] — 2026-05-07
### Fixed
- `agnes query` against a `query_mode='remote'` table previously surfaced DuckDB's misleading "did you mean <similar materialized table>" suggestion. Now appends a friendlier hint pointing users to `agnes catalog`, `agnes schema <id>`, and `agnes query --remote`. Reproduces from a real analyst session where `DESCRIBE unit_economics` (a remote table) sent the user down a 30-second wrong path.
## [0.46.1] — 2026-05-07
### Fixed

View file

@ -2,6 +2,7 @@
import json
import os
import re
import sys
from pathlib import Path
from typing import List, Optional
@ -78,6 +79,26 @@ def _query_local(sql: str, fmt: str, limit: int):
_output(columns, result, fmt)
except Exception as e:
typer.echo(f"Query error: {e}", err=True)
# DuckDB's "Did you mean <similar materialized view>" suggestion is
# misleading when the unresolvable identifier is actually a
# `query_mode='remote'` table — those have no local view by design.
# Append a friendly hint pointing the user at `agnes catalog`,
# `agnes schema`, and `agnes query --remote`. We don't verify against
# the remote registry here (this command is offline-friendly), so the
# hint is conditional ("might be") — safe even when the name was just
# a typo.
m = re.search(r"Table with name ([A-Za-z_][A-Za-z0-9_]*) does not exist", str(e))
if m:
typer.echo("", err=True)
typer.echo(
f"Note: `{m.group(1)}` might be a `query_mode='remote'` table. Local "
"DuckDB only holds views for `local` and `materialized` tables — "
"`remote` ones live on BigQuery and are not synced.\n"
" - List all registered tables: agnes catalog\n"
" - Inspect column schema: agnes schema <name>\n"
" - Run a query against BigQuery: agnes query --remote \"<SQL>\"",
err=True,
)
raise typer.Exit(1)
finally:
conn.close()

View file

@ -1,6 +1,6 @@
[project]
name = "agnes-the-ai-analyst"
version = "0.46.1"
version = "0.46.2"
description = "Agnes — AI Data Analyst platform for AI analytical systems"
requires-python = ">=3.11,<3.14"
license = "MIT"

View file

@ -150,3 +150,46 @@ class TestLocalQuery:
result = runner.invoke(app, ["query", "SELECT * FROM nonexistent_table_xyz"])
assert result.exit_code == 1
assert "Query error" in result.output
def test_local_query_missing_table_hints_remote(self, tmp_config):
"""Querying a table absent from local DuckDB surfaces a hint about
`query_mode='remote'` tables alongside the original DuckDB error.
Reproduces the analyst-session UX gap where DuckDB's nearest-name
("Did you mean <other_table>") suggestion sent the user down the
wrong path they thought the table didn't exist or they typo'd,
when in fact it's a remote table that intentionally has no local
view.
"""
import duckdb
db_dir = tmp_config / "local" / "user" / "duckdb"
db_dir.mkdir(parents=True)
duckdb.connect(str(db_dir / "analytics.duckdb")).close()
result = runner.invoke(app, ["query", "DESCRIBE unit_economics"])
assert result.exit_code == 1
# Original DuckDB diagnostic must remain visible (don't break logging).
assert "Query error" in result.output
assert "Table with name unit_economics does not exist" in result.output
# New hint fires.
assert "query_mode='remote'" in result.output
assert "agnes catalog" in result.output
assert "agnes schema" in result.output
assert "agnes query --remote" in result.output
def test_local_query_syntax_error_does_not_show_remote_hint(self, tmp_config):
"""A non-missing-table failure (e.g. raw syntax error) must NOT
trigger the new remote-mode hint the regex only matches DuckDB's
`Table with name X does not exist` shape.
"""
import duckdb
db_dir = tmp_config / "local" / "user" / "duckdb"
db_dir.mkdir(parents=True)
duckdb.connect(str(db_dir / "analytics.duckdb")).close()
# Trailing FROM with no relation -> ParserException, not CatalogException.
result = runner.invoke(app, ["query", "SELECT * FROM"])
assert result.exit_code == 1
assert "Query error" in result.output
assert "query_mode='remote'" not in result.output
assert "agnes query --remote" not in result.output