This is an automated email from the ASF dual-hosted git repository.
shahar1 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 4bda61c8c3d Keep the HTTPError when a Dataprep error body is not JSON
(#71702)
4bda61c8c3d is described below
commit 4bda61c8c3d997a90403d7da453cb3bc78a85f21
Author: Tushar Pagar <[email protected]>
AuthorDate: Sun Oct 18 14:19:44 2026 +0530
Keep the HTTPError when a Dataprep error body is not JSON (#71702)
Preserve Dataprep HTTP errors when response bodies are not JSON
Fall back to the raw response body when extracting the error message, so
malformed or non-object JSON cannot mask the original `HTTPError.
---------
Co-authored-by: Shahar Epstein <[email protected]>
---
.../providers/google/cloud/hooks/dataprep.py | 16 ++++++++++-
.../tests/unit/google/cloud/hooks/test_dataprep.py | 31 ++++++++++++++++++++++
2 files changed, 46 insertions(+), 1 deletion(-)
diff --git
a/providers/google/src/airflow/providers/google/cloud/hooks/dataprep.py
b/providers/google/src/airflow/providers/google/cloud/hooks/dataprep.py
index 34a33a146d8..3ac825bb8c9 100644
--- a/providers/google/src/airflow/providers/google/cloud/hooks/dataprep.py
+++ b/providers/google/src/airflow/providers/google/cloud/hooks/dataprep.py
@@ -219,9 +219,23 @@ class GoogleDataprepHook(BaseHook):
try:
response.raise_for_status()
except HTTPError:
- self.log.error(response.json().get("exception"))
+ self.log.error(self._get_error_message(response))
raise
+ @staticmethod
+ def _get_error_message(response: requests.models.Response) -> Any:
+ """
+ Return the error detail from the response, falling back to its raw
body.
+
+ An error response does not always carry a JSON object: a proxy or
gateway
+ can answer with HTML or an empty payload, and parsing that here would
+ replace the HTTPError we are about to re-raise.
+ """
+ try:
+ return response.json().get("exception")
+ except (ValueError, AttributeError):
+ return response.text
+
@retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1,
max=10))
def create_imported_dataset(self, *, body_request: dict) -> dict:
"""
diff --git a/providers/google/tests/unit/google/cloud/hooks/test_dataprep.py
b/providers/google/tests/unit/google/cloud/hooks/test_dataprep.py
index 29a5049f4fd..b9d77f83c7a 100644
--- a/providers/google/tests/unit/google/cloud/hooks/test_dataprep.py
+++ b/providers/google/tests/unit/google/cloud/hooks/test_dataprep.py
@@ -23,7 +23,9 @@ from unittest import mock
from unittest.mock import patch
import pytest
+import requests
from requests import HTTPError
+from requests.exceptions import JSONDecodeError
from tenacity import RetryError
from airflow.providers.google.cloud.hooks.dataprep import GoogleDataprepHook
@@ -852,3 +854,32 @@ class TestGoogleDataprepFlowPathHooks:
)
assert "HTTPError" in str(ctx.value)
assert mock_get_request.call_count == 5
+
+
+class TestGoogleDataprepHookRaiseForStatus:
+ def setup_method(self):
+ with mock.patch(f"{BASEHOOK_PATCH_PATH}.get_connection") as conn:
+ conn.return_value.extra_dejson = EXTRA
+ self.hook = GoogleDataprepHook(dataprep_conn_id="dataprep_default")
+
+ @pytest.mark.parametrize(
+ ("json_kwargs", "text"),
+ [
+ pytest.param(
+ {"side_effect": JSONDecodeError("Expecting value", "", 0)},
+ "<html>502 Bad Gateway</html>",
+ id="body-is-not-json",
+ ),
+ pytest.param({"return_value": ["boom"]}, '["boom"]',
id="body-is-not-an-object"),
+ ],
+ )
+ @mock.patch.object(GoogleDataprepHook, "log",
new_callable=mock.PropertyMock)
+ def test_raises_http_error_and_logs_raw_body(self, mock_log, json_kwargs,
text):
+ response = mock.create_autospec(requests.Response, instance=True)
+ response.raise_for_status.side_effect = HTTPError()
+ response.json.configure_mock(**json_kwargs)
+ response.text = text
+
+ with pytest.raises(HTTPError):
+ self.hook._raise_for_status(response)
+ mock_log.return_value.error.assert_called_once_with(text)