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)

Reply via email to