Skip to content

Commit 08483fb

Browse files
Linchinchalmerlowe
andauthored
fix: add detailed message in job error (#1762)
* fix: more detailed job error message * lint * fix mypy error * remove import ignore * Update google/cloud/bigquery/job/base.py Co-authored-by: Chalmer Lowe <[email protected]> * Update google/cloud/bigquery/job/base.py Co-authored-by: Chalmer Lowe <[email protected]> * variable name and unit test --------- Co-authored-by: Chalmer Lowe <[email protected]> Co-authored-by: Chalmer Lowe <[email protected]>
1 parent 575f7fc commit 08483fb

File tree

2 files changed

+43
-3
lines changed

2 files changed

+43
-3
lines changed

google/cloud/bigquery/job/base.py

+22-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
}
5656

5757

58-
def _error_result_to_exception(error_result):
58+
def _error_result_to_exception(error_result, errors=None):
5959
"""Maps BigQuery error reasons to an exception.
6060
6161
The reasons and their matching HTTP status codes are documented on
@@ -66,6 +66,7 @@ def _error_result_to_exception(error_result):
6666
6767
Args:
6868
error_result (Mapping[str, str]): The error result from BigQuery.
69+
errors (Union[Iterable[str], None]): The detailed error messages.
6970
7071
Returns:
7172
google.cloud.exceptions.GoogleAPICallError: The mapped exception.
@@ -74,8 +75,24 @@ def _error_result_to_exception(error_result):
7475
status_code = _ERROR_REASON_TO_EXCEPTION.get(
7576
reason, http.client.INTERNAL_SERVER_ERROR
7677
)
78+
# Manually create error message to preserve both error_result and errors.
79+
# Can be removed once b/310544564 and b/318889899 are resolved.
80+
concatenated_errors = ""
81+
if errors:
82+
concatenated_errors = "; "
83+
for err in errors:
84+
concatenated_errors += ", ".join(
85+
[f"{key}: {value}" for key, value in err.items()]
86+
)
87+
concatenated_errors += "; "
88+
89+
# strips off the last unneeded semicolon and space
90+
concatenated_errors = concatenated_errors[:-2]
91+
92+
error_message = error_result.get("message", "") + concatenated_errors
93+
7794
return exceptions.from_http_status(
78-
status_code, error_result.get("message", ""), errors=[error_result]
95+
status_code, error_message, errors=[error_result]
7996
)
8097

8198

@@ -886,7 +903,9 @@ def _set_future_result(self):
886903
return
887904

888905
if self.error_result is not None:
889-
exception = _error_result_to_exception(self.error_result)
906+
exception = _error_result_to_exception(
907+
self.error_result, self.errors or ()
908+
)
890909
self.set_exception(exception)
891910
else:
892911
self.set_result(self)

tests/unit/job/test_base.py

+21
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,27 @@ def test_missing_reason(self):
4747
exception = self._call_fut(error_result)
4848
self.assertEqual(exception.code, http.client.INTERNAL_SERVER_ERROR)
4949

50+
def test_contatenate_errors(self):
51+
# Added test for b/310544564 and b/318889899.
52+
# Ensures that error messages from both error_result and errors are
53+
# present in the exception raised.
54+
55+
error_result = {
56+
"reason": "invalid1",
57+
"message": "error message 1",
58+
}
59+
errors = [
60+
{"reason": "invalid2", "message": "error message 2"},
61+
{"reason": "invalid3", "message": "error message 3"},
62+
]
63+
64+
exception = self._call_fut(error_result, errors)
65+
self.assertEqual(
66+
exception.message,
67+
"error message 1; reason: invalid2, message: error message 2; "
68+
"reason: invalid3, message: error message 3",
69+
)
70+
5071

5172
class Test_JobReference(unittest.TestCase):
5273
JOB_ID = "job-id"

0 commit comments

Comments
 (0)