kaxil opened a new pull request, #76783: URL: https://github.com/apache/airflow/pull/76783
`SQLToolset` turned every exception into a `ModelRetry`, including an unreachable host, rejected credentials and a missing connection. The model can't fix any of those. Running against a SQLite file that couldn't be opened, `gpt-5.4-mini` replied "I couldn't access the database... try again once the connection issue is fixed" and the run succeeded, so the task finished green with that sentence as its output and Airflow never retried it. Claude Opus 5.5 called `list_tables`, `query` and `get_schema` one after another until it ran out of retries. With durable execution there's a second problem: the `ModelRetry` is recorded, so the retry after the outage replays the failure to the model instead of querying again. With this change, failing to reach the database raises and fails the attempt. Errors from the statement itself still go back to the model as `ModelRetry`, so it can fix its SQL, and that correction still replays under durable execution. A raised call isn't recorded, so the retry queries the database again. ## Design rationale DBAPI exception classes can't separate the two cases (SQLite raises `OperationalError` for both "no such column" and "unable to open database file"), so the toolset looks at where the error came from instead: - Looking up the connection and its hook. A connection that isn't defined, or isn't a `DbApiHook`, raises. - Building the hook's URI and getting a connection. `DbApiHook.run`, and the Snowflake, Databricks and Exasol overrides, connect through `self.get_conn()`, so the toolset wraps `get_conn` on its own hook instance. The URI is built first because for Databricks that fetches a token. A failure in either raises `Could not reach the database of connection '<id>': <driver message>`. - `list_tables` and `get_schema` use the SQLAlchemy inspector. Building it (loading the dialect, connecting) raises, as does a `DBAPIError` with no statement or with `connection_invalidated` set. Everything after the connection is open counts as the statement's error. A connection that drops while a `query` is running still goes to the model. `DbApiHook.run` passes back the raw driver exception, and most dialects can't tell a disconnect from it alone: psycopg 3's `is_disconnect` needs the live connection, which `run` has already closed, and the Snowflake and BigQuery dialects don't implement it. The model usually just runs the query again, on a new connection. Hooks whose `get_conn` doesn't touch the network behave the same way. The SQL toolset docs cover both cases. Behaviour change: an outage now fails the attempt on the first tool call, where before it either used up the model's retries or let the run finish without data. There are no new config options or APIs. ## Known issues The split doesn't apply inside CodeMode. pydantic-ai-harness turns any exception a tool raises from `run_code` into a `ModelRetry` (the `MontyRuntimeError` branch in `code_mode/_toolset.py`), and that was true before this PR too. The CodeMode and SQL toolset pages now say so. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
