diff --git a/src/firebolt/utils/util.py b/src/firebolt/utils/util.py index 089e103e98d..83958fa3b94 100644 --- a/src/firebolt/utils/util.py +++ b/src/firebolt/utils/util.py @@ -19,6 +19,7 @@ from firebolt.utils.exception import ( ConfigurationError, + FireboltError, FireboltStructuredError, ) @@ -171,6 +172,10 @@ def raise_error_from_response(resp: Response) -> None: resp (Response): HTTP response """ to_raise = None + # If error is Text - raise as is + if "text/plain" in resp.headers.get("Content-Type", ""): + raise FireboltError(resp.text) + # If error is Json - parse it and raise try: decoded = resp.json() if "errors" in decoded and len(decoded["errors"]) > 0: @@ -186,6 +191,7 @@ def raise_error_from_response(resp: Response) -> None: raise to_raise # Raise status error if no error info was found in the body + # This error does not contain the response body resp.raise_for_status() diff --git a/tests/integration/dbapi/async/V2/test_queries_async.py b/tests/integration/dbapi/async/V2/test_queries_async.py index 3167b14e32c..b67a682c32d 100644 --- a/tests/integration/dbapi/async/V2/test_queries_async.py +++ b/tests/integration/dbapi/async/V2/test_queries_async.py @@ -13,7 +13,7 @@ from firebolt.client.auth.base import Auth from firebolt.common._types import ColType from firebolt.common.row_set.types import Column -from firebolt.utils.exception import FireboltStructuredError +from firebolt.utils.exception import FireboltError, FireboltStructuredError from tests.integration.dbapi.conftest import LONG_SELECT_DEFAULT_V2 from tests.integration.dbapi.utils import assert_deep_eq @@ -435,7 +435,7 @@ async def test_multi_statement_query(connection: Connection) -> None: async def test_set_invalid_parameter(connection: Connection): async with connection.cursor() as c: assert len(c._set_parameters) == 0 - with raises((OperationalError, FireboltStructuredError)) as e: + with raises((OperationalError, FireboltError)) as e: await c.execute("SET some_invalid_parameter = 1") assert "Unknown setting" in str(e.value) or "query param not allowed" in str( diff --git a/tests/integration/dbapi/sync/V2/test_queries.py b/tests/integration/dbapi/sync/V2/test_queries.py index 81fd37f657f..b61ad2c5b41 100644 --- a/tests/integration/dbapi/sync/V2/test_queries.py +++ b/tests/integration/dbapi/sync/V2/test_queries.py @@ -13,7 +13,7 @@ from firebolt.common._types import ColType from firebolt.common.row_set.types import Column from firebolt.db import Binary, Connection, Cursor, OperationalError, connect -from firebolt.utils.exception import FireboltStructuredError +from firebolt.utils.exception import FireboltError, FireboltStructuredError from tests.integration.dbapi.conftest import LONG_SELECT_DEFAULT_V2 from tests.integration.dbapi.utils import assert_deep_eq @@ -435,7 +435,7 @@ def test_multi_statement_query(connection: Connection) -> None: def test_set_invalid_parameter(connection: Connection): with connection.cursor() as c: assert len(c._set_parameters) == 0 - with raises((OperationalError, FireboltStructuredError)) as e: + with raises((OperationalError, FireboltError)) as e: c.execute("set some_invalid_parameter = 1") assert "Unknown setting" in str(e.value) or "query param not allowed" in str( e.value diff --git a/tests/unit/async_db/test_cursor.py b/tests/unit/async_db/test_cursor.py index 140c14fc932..86e9bc24347 100644 --- a/tests/unit/async_db/test_cursor.py +++ b/tests/unit/async_db/test_cursor.py @@ -1915,3 +1915,26 @@ async def test_transaction_params_included_in_query_requests( ) await cursor.execute("SELECT 2") + + +async def test_cursor_plaintext_error( + httpx_mock: HTTPXMock, + cursor: Cursor, + query_url: str, +): + """Test handling of plaintext error responses from the server.""" + httpx_mock.add_callback( + lambda *args, **kwargs: Response( + status_code=codes.NOT_FOUND, + text="Plaintext error message", + headers={"Content-Type": "text/plain"}, + ), + url=query_url, + ) + with raises(FireboltError) as excinfo: + await cursor.execute("select * from t") + + assert cursor._state == CursorState.ERROR + assert "Plaintext error message" in str( + excinfo.value + ), "Invalid error message for plaintext error response" \ No newline at end of file diff --git a/tests/unit/db/test_cursor.py b/tests/unit/db/test_cursor.py index 23b5532fcc2..5e4322b8d7b 100644 --- a/tests/unit/db/test_cursor.py +++ b/tests/unit/db/test_cursor.py @@ -1803,3 +1803,26 @@ def test_transaction_params_included_in_query_requests( # This will only succeed if transaction parameters are properly passed cursor.execute("SELECT 2") + + +def test_cursor_plaintext_error( + httpx_mock: HTTPXMock, + cursor: Cursor, + query_url: str, +): + """Test handling of plaintext error responses from the server.""" + httpx_mock.add_callback( + lambda *args, **kwargs: Response( + status_code=codes.NOT_FOUND, + text="Plaintext error message", + headers={"Content-Type": "text/plain"}, + ), + url=query_url, + ) + with raises(FireboltError) as excinfo: + cursor.execute("select * from t") + + assert cursor._state == CursorState.ERROR + assert "Plaintext error message" in str( + excinfo.value + ), "Invalid error message for plaintext error response" \ No newline at end of file