Skip to content

Shared httpx.Client causes base_url clobber between PostgREST and Storage - results in 404 and KeyError #1244

@jwstanly

Description

@jwstanly

Describe the bug

When passing a custom httpx.Client via SyncClientOptions(httpx_client=...), the same client instance is shared across all Supabase services (PostgREST, Storage, Auth, Functions). Each service mutates the httpx.Client.base_url when initialized, causing subsequent requests to other services to hit the wrong API endpoints.

Symptoms:

  • 404 errors when Storage methods hit /rest/v1/object/list/... instead of /storage/v1/object/list/...
  • Cryptic KeyError: 'error' because the 404 response format differs between PostgREST and Storage APIs
  • Non-deterministic failures depending on which service was accessed most recently

Root Cause:

The issue is in how storage3 and postgrest clients handle injected httpx_client:

storage3/_sync/client.py:77-80

if http_client is not None:
    http_client.base_url = base_url  # Mutates shared client!
    http_client.headers.update({**headers})
    return http_client

Timeline:

  1. Storage service initialized → httpx_client.base_url = ".../storage/v1/" (Works)
  2. PostgREST service accessed → httpx_client.base_url = ".../rest/v1/" Clobbers storage URL
  3. Storage methods called → still points to /rest/v1/ → 404 → KeyError: 'error'

To Reproduce

Steps to reproduce the behavior:

import httpx
from supabase import Client, create_client
from supabase.lib.client_options import SyncClientOptions

url = "https://your-project.supabase.co"
key = "your-anon-key"

# Create client with shared httpx instance
timeout = httpx.Timeout(10.0, read=60.0)
httpx_client = httpx.Client(timeout=timeout)
options = SyncClientOptions(httpx_client=httpx_client)
client: Client = create_client(url, key, options=options)

bucket = client.storage.from_("your-bucket")
print(f"Initial base_url: {bucket._client.base_url}")
# Output: https://your-project.supabase.co/storage/v1/

# First storage call works
bucket.list("some-path/")  # Works

# Touch PostgREST (causes base_url mutation)
_ = client.postgrest
print(f"After postgrest: {bucket._client.base_url}")
# Output: https://your-project.supabase.co/rest/v1/  WRONG!

# Second storage call fails
bucket.list("some-path/")  # KeyError: 'error'

Expected behavior

Storage requests should always hit /storage/v1/ endpoints regardless of whether PostgREST or other services have been accessed. The second bucket.list() call should succeed with the same results as the first call.

Actual behavior: After accessing PostgREST, Storage requests hit /rest/v1/ endpoints, causing 404 errors and KeyError: 'error' exceptions.

Screenshots

Terminal output demonstrating the bug:

# Demonstrating base_url mutation
bucket = client.storage.from_("snapshots")
print(f"Initial base_url: {bucket._client.base_url}")
# Output: https://your-project.supabase.co/storage/v1/

bucket.list("some-path/")  # Works

_ = client.postgrest  # Touch PostgREST
print(f"After accessing postgrest: {bucket._client.base_url}")
# Output: https://your-project.supabase.co/rest/v1/  CLOBBERED!

bucket.list("some-path/")  # Fails with KeyError: 'error'

Error output:

KeyError: 'error'
  File "storage3/_sync/file_api.py", line 53, in _request
    response.raise_for_status()
  File "httpx/_models.py", line 829, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '404 Not Found' for url 'https://your-project.supabase.co/rest/v1/object/list/snapshots'

During handling of the above exception, another exception occurred:
  File "storage3/_sync/file_api.py", line 56, in _request
    raise StorageApiError(resp["message"], resp["error"], resp["statusCode"])
KeyError: 'error'

System information

  • OS: macOS 14.5 / Linux (Ubuntu 22.04)
  • Version of supabase-py: 2.x (latest)
  • Version of storage3: 0.x (latest)
  • Version of Python: 3.12.9
  • httpx version: (latest)

Additional context

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingpythonPull requests that update Python codestorageIssues related to storage-py

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions