Bug Report
Summary
The Python SDK constructs login URLs via f-string concatenation (f"{self.settings.base_url}/api/{self.api_version}/login"), which produces double-slash URLs when base_url has a trailing slash. This results in a 403 Forbidden from the Looker server.
This was previously masked by the requests library (pre-2.34.0) which silently normalized // to / in URL paths. As of requests 2.34.0, this normalization was removed, exposing the bug.
Steps to Reproduce
import os
os.environ['LOOKERSDK_CLIENT_ID'] = '<valid_id>'
os.environ['LOOKERSDK_CLIENT_SECRET'] = '<valid_secret>'
os.environ['LOOKERSDK_BASE_URL'] = 'https://my-instance.looker.com/' # note trailing slash
import looker_sdk
sdk = looker_sdk.init40()
me = sdk.me() # raises SDKError with empty message
Expected Behavior
Authentication succeeds regardless of whether base_url has a trailing slash.
Actual Behavior
The SDK sends POST //api/4.0/login (double slash), which the Looker server rejects with 403 Forbidden and an empty response body. The SDK surfaces this as:
looker_sdk.error.SDKError:
message:
documentation_url:
error_doc_url:
error details:
Root Cause
In python/looker_sdk/rtl/auth_session.py, the login URL is constructed via f-string concatenation:
# auth_session.py, _login() method
f"{self.settings.base_url}/api/{self.api_version}/login"
This does not strip trailing slashes from base_url. When base_url = "https://example.com/", the result is "https://example.com//api/4.0/login".
By contrast, api_methods.py correctly uses urllib.parse.urljoin():
# api_methods.py
self.api_path = urllib.parse.urljoin(auth.settings.base_url, f"/api/{api_version}/")
Suggested Fix
Strip the trailing slash from base_url during settings initialization in api_settings.py:
# In ApiSettings.__init__()
self.base_url = data.get("base_url", "").rstrip("/")
This is the simplest fix and makes all downstream URL construction safe. The same pattern (rstrip("/")) is already used by many other SDK libraries.
Alternatively, auth_session.py could use urllib.parse.urljoin() consistently, matching the approach in api_methods.py.
Environment
- looker-sdk: 26.8.0 (also affects 26.6.1 and likely all versions)
- Python: 3.14.4
- requests: 2.34.0 (bug was masked in ≤2.33.1)
- Looker instance: Google Cloud hosted
Workaround
Remove the trailing slash from LOOKERSDK_BASE_URL:
LOOKERSDK_BASE_URL=https://my-instance.looker.com # no trailing slash
Bug Report
Summary
The Python SDK constructs login URLs via f-string concatenation (
f"{self.settings.base_url}/api/{self.api_version}/login"), which produces double-slash URLs whenbase_urlhas a trailing slash. This results in a 403 Forbidden from the Looker server.This was previously masked by the
requestslibrary (pre-2.34.0) which silently normalized//to/in URL paths. As ofrequests 2.34.0, this normalization was removed, exposing the bug.Steps to Reproduce
Expected Behavior
Authentication succeeds regardless of whether
base_urlhas a trailing slash.Actual Behavior
The SDK sends
POST //api/4.0/login(double slash), which the Looker server rejects with 403 Forbidden and an empty response body. The SDK surfaces this as:Root Cause
In
python/looker_sdk/rtl/auth_session.py, the login URL is constructed via f-string concatenation:This does not strip trailing slashes from
base_url. Whenbase_url = "https://example.com/", the result is"https://example.com//api/4.0/login".By contrast,
api_methods.pycorrectly usesurllib.parse.urljoin():Suggested Fix
Strip the trailing slash from
base_urlduring settings initialization inapi_settings.py:This is the simplest fix and makes all downstream URL construction safe. The same pattern (
rstrip("/")) is already used by many other SDK libraries.Alternatively,
auth_session.pycould useurllib.parse.urljoin()consistently, matching the approach inapi_methods.py.Environment
Workaround
Remove the trailing slash from
LOOKERSDK_BASE_URL: