This is an automated email from the ASF dual-hosted git repository.

amoghrajesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 3e48e8d852a Surface databricks API errors as a separate exception 
(#69019)
3e48e8d852a is described below

commit 3e48e8d852a8762bad5e76106a557067e05b3b0f
Author: Amogh Desai <[email protected]>
AuthorDate: Fri Jun 26 15:16:57 2026 +0530

    Surface databricks API errors as a separate exception (#69019)
---
 .../src/airflow/providers/databricks/exceptions.py         |  8 ++++++++
 .../airflow/providers/databricks/hooks/databricks_base.py  |  7 +++++--
 .../tests/unit/databricks/hooks/test_databricks.py         | 14 ++++++++++++++
 scripts/ci/prek/known_airflow_exceptions.txt               |  2 +-
 4 files changed, 28 insertions(+), 3 deletions(-)

diff --git 
a/providers/databricks/src/airflow/providers/databricks/exceptions.py 
b/providers/databricks/src/airflow/providers/databricks/exceptions.py
index 59c8f3fb606..0831810f70f 100644
--- a/providers/databricks/src/airflow/providers/databricks/exceptions.py
+++ b/providers/databricks/src/airflow/providers/databricks/exceptions.py
@@ -34,3 +34,11 @@ class 
DatabricksSqlExecutionTimeout(DatabricksSqlExecutionError):
 
 class DatabricksOperatorPayloadError(AirflowException):
     """Raised when a Databricks operator payload is invalid."""
+
+
+class DatabricksApiError(AirflowException):
+    """Raised when a Databricks REST API call returns an error response."""
+
+    def __init__(self, message: str, *, http_status_code: int | None = None) 
-> None:
+        super().__init__(message)
+        self.http_status_code = http_status_code
diff --git 
a/providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py
 
b/providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py
index 7ab51fc7dfa..f846ab23a54 100644
--- 
a/providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py
+++ 
b/providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py
@@ -51,6 +51,7 @@ from tenacity import (
 
 from airflow import __version__
 from airflow.providers.common.compat.sdk import AirflowException, 
AirflowOptionalProviderFeatureException
+from airflow.providers.databricks.exceptions import DatabricksApiError
 from airflow.providers_manager import ProvidersManager
 
 try:
@@ -1150,7 +1151,7 @@ class BaseDatabricksHook(BaseHook):
         except requests_exceptions.HTTPError as e:
             if wrap_http_errors:
                 msg = f"Response: {e.response.content.decode()}, Status Code: 
{e.response.status_code}"
-                raise AirflowException(msg)
+                raise DatabricksApiError(msg, 
http_status_code=e.response.status_code) from e
             raise
 
     async def _a_do_api_call(self, endpoint_info: tuple[str, str], json: 
dict[str, Any] | None = None):
@@ -1211,7 +1212,9 @@ class BaseDatabricksHook(BaseHook):
         except RetryError:
             raise AirflowException(f"API requests to Databricks failed 
{self.retry_limit} times. Giving up.")
         except aiohttp.ClientResponseError as err:
-            raise AirflowException(f"Response: {err.message}, Status Code: 
{err.status}")
+            raise DatabricksApiError(
+                f"Response: {err.message}, Status Code: {err.status}", 
http_status_code=err.status
+            ) from err
 
     @staticmethod
     def _get_error_code(exception: BaseException) -> str:
diff --git 
a/providers/databricks/tests/unit/databricks/hooks/test_databricks.py 
b/providers/databricks/tests/unit/databricks/hooks/test_databricks.py
index 558e39a2129..1a03934ec83 100644
--- a/providers/databricks/tests/unit/databricks/hooks/test_databricks.py
+++ b/providers/databricks/tests/unit/databricks/hooks/test_databricks.py
@@ -35,6 +35,7 @@ from requests.auth import HTTPBasicAuth
 
 from airflow.models import Connection
 from airflow.providers.common.compat.sdk import AirflowException
+from airflow.providers.databricks.exceptions import DatabricksApiError
 from airflow.providers.databricks.hooks.databricks import (
     GET_RUN_ENDPOINT,
     SUBMIT_RUN_ENDPOINT,
@@ -404,6 +405,19 @@ class TestDatabricksHook:
 
             mock_errors.assert_not_called()
 
+    @mock.patch("airflow.providers.databricks.hooks.databricks_base.requests")
+    def test_failing_do_api_call_raises_exception(self, mock_requests):
+        hook = DatabricksHook(retry_args=DEFAULT_RETRY_ARGS)
+
+        setup_mock_requests(mock_requests, requests_exceptions.HTTPError, 
status_code=404)
+
+        with pytest.raises(DatabricksApiError) as exc_info:
+            hook._do_api_call(SUBMIT_RUN_ENDPOINT, {})
+
+        assert exc_info.value.http_status_code == 404
+        # simple backcompat check, so that we do not break existing code that 
expects AirflowException
+        assert isinstance(exc_info.value, AirflowException)
+
     def test_do_api_call_succeeds_after_retrying(self):
         hook = DatabricksHook(retry_args=DEFAULT_RETRY_ARGS)
 
diff --git a/scripts/ci/prek/known_airflow_exceptions.txt 
b/scripts/ci/prek/known_airflow_exceptions.txt
index cf9bb1d8647..1fb46185490 100644
--- a/scripts/ci/prek/known_airflow_exceptions.txt
+++ b/scripts/ci/prek/known_airflow_exceptions.txt
@@ -174,7 +174,7 @@ 
providers/common/sql/src/airflow/providers/common/sql/operators/sql.py::7
 providers/common/sql/src/airflow/providers/common/sql/sensors/sql.py::6
 providers/common/sql/src/airflow/providers/common/sql/triggers/sql.py::1
 providers/databricks/src/airflow/providers/databricks/hooks/databricks.py::8
-providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py::46
+providers/databricks/src/airflow/providers/databricks/hooks/databricks_base.py::44
 
providers/databricks/src/airflow/providers/databricks/hooks/databricks_sql.py::2
 
providers/databricks/src/airflow/providers/databricks/operators/databricks.py::6
 
providers/databricks/src/airflow/providers/databricks/operators/databricks_repos.py::12

Reply via email to