From 72ec2bc97895a8b13fbec25cd15856df0f03c686 Mon Sep 17 00:00:00 2001 From: RapidPoseidon Date: Fri, 24 Apr 2026 09:15:03 +0000 Subject: [PATCH] fix(auth): handle malformed bridge-token responses gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_get_bridge_tokens` only caught `requests.RequestException`, so a response that parsed successfully but was missing `readKey`/`writeKey` (or wasn't JSON at all) raised `KeyError` / `ValueError` and surfaced a confusing traceback at SDK boot time. Catch `ValueError` from `response.json()` and `KeyError`/`TypeError` from the `BridgeToken` construction, log a useful diagnostic (including the keys that *were* present), and return `None` — the caller already treats `None` as the no-auth-available signal and falls through to a readable error path. Session: https://session-bc38cc85.poseidon.rapidata.internal/ Co-Authored-By: Claude Opus 4.7 Co-Authored-By: lino --- src/rapidata/service/credential_manager.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/rapidata/service/credential_manager.py b/src/rapidata/service/credential_manager.py index cf659ca31..5cecc7164 100644 --- a/src/rapidata/service/credential_manager.py +++ b/src/rapidata/service/credential_manager.py @@ -135,20 +135,33 @@ def reset_credentials(self) -> None: def _get_bridge_tokens(self) -> Optional[BridgeToken]: """Get bridge tokens from the identity endpoint.""" logger.debug("Getting bridge tokens") + bridge_endpoint = ( + f"{self.endpoint}/identity/bridge-token?clientId=rapidata-cli" + ) try: - bridge_endpoint = ( - f"{self.endpoint}/identity/bridge-token?clientId=rapidata-cli" - ) response = requests.post(bridge_endpoint, verify=self.cert_path) if not response.ok: logger.error("Failed to get bridge tokens: %s", response.status_code) return None data = response.json() - return BridgeToken(read_key=data["readKey"], write_key=data["writeKey"]) except requests.RequestException as e: logger.error("Failed to get bridge tokens: %s", e) return None + except ValueError as e: + # `response.json()` raises ValueError on malformed bodies. + logger.error("Bridge token response was not valid JSON: %s", e) + return None + + try: + return BridgeToken(read_key=data["readKey"], write_key=data["writeKey"]) + except (KeyError, TypeError) as e: + logger.error( + "Bridge token response missing expected keys (got: %s): %s", + list(data.keys()) if isinstance(data, dict) else type(data).__name__, + e, + ) + return None def _poll_read_key(self, read_key: str) -> Optional[str]: """Poll the read key endpoint until we get an access token."""