Skip to content

Python SDK: trailing slash in base_url causes double-slash URLs and auth failure #1714

@RickardNordnet

Description

@RickardNordnet

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions