-
-
Notifications
You must be signed in to change notification settings - Fork 366
Description
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:
- Storage service initialized →
httpx_client.base_url = ".../storage/v1/"
(Works) - PostgREST service accessed →
httpx_client.base_url = ".../rest/v1/"
Clobbers storage URL - 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)