From bf63f32da88bbc6c2c1f7202786729d96881c35f Mon Sep 17 00:00:00 2001 From: Clay McGinnis Date: Wed, 21 Oct 2026 12:09:40 -0700 Subject: [PATCH] fix: keep the API's suggestion in session creation errors A refused POST /sql/session (for example, an exceeded quota) carries a suggestion telling the user how to resolve it. The driver dropped it, so callers such as the Wherobots MCP server could not relay it. --- tests/test_driver.py | 25 +++++++++++++++++++++++++ wherobots/db/driver.py | 6 +++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/tests/test_driver.py b/tests/test_driver.py index 5bf1907..3b51d51 100644 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -316,6 +316,31 @@ def test_http_timeout_on_post(self, mock_post): _, kwargs = mock_post.call_args assert kwargs["timeout"] == DEFAULT_HTTP_TIMEOUT + @patch("wherobots.db.driver.requests.post") + def test_session_error_keeps_suggestion(self, mock_post): + """A refused session's error relays the API's suggestion.""" + post_resp = MagicMock() + post_resp.status_code = 403 + post_resp.raise_for_status.side_effect = requests.HTTPError(response=post_resp) + post_resp.json.return_value = { + "errors": [ + { + "message": "Insufficient Quota", + "details": "The total requested cpu exceed the quota", + "suggestion": "Contact us to request a quota increase.", + } + ] + } + mock_post.return_value = post_resp + + with pytest.raises(InterfaceError) as exc_info: + connect(api_key="test-key") + + assert str(exc_info.value) == ( + "Failed to create SQL session: Insufficient Quota: The total " + "requested cpu exceed the quota. Contact us to request a quota increase." + ) + @patch("wherobots.db.driver.requests.get") @patch("wherobots.db.driver.requests.post") def test_http_timeout_on_get(self, mock_post, mock_get): diff --git a/wherobots/db/driver.py b/wherobots/db/driver.py index 622577c..b8786fc 100644 --- a/wherobots/db/driver.py +++ b/wherobots/db/driver.py @@ -239,7 +239,11 @@ def connect( info = e.response.json() errors = info.get("errors", []) if errors and isinstance(errors, list): - details = f"{errors[0]['message']}: {errors[0]['details']}" + error = errors[0] + details = f"{error['message']}: {error['details']}" + # The way out of the error, e.g. how to raise a quota. + if suggestion := error.get("suggestion"): + details = f"{details.rstrip('.')}. {suggestion}" except requests.JSONDecodeError: pass raise InterfaceError(f"Failed to create SQL session: {details}") from e