JuliusBrussee/caveman · error · TypeError
Expected an HTTPX2 AsyncBaseTransport
Error message
Expected an HTTPX2 AsyncBaseTransport
What it means
CavemanAsyncOpenAITransport is the async counterpart of the OpenAI interception transport and delegates handle_async_request to the supplied transport. It requires an httpx2.AsyncBaseTransport so delegation is guaranteed to support async handling.
Solutions
- Pass an httpx2.AsyncBaseTransport, e.g. httpx2.AsyncHTTPTransport()
- Swap the sync CavemanOpenAITransport usage for the async class only with async transports
- Verify imports point at httpx2, not httpx
- isinstance(transport, httpx2.AsyncBaseTransport) check before construction
Example fix
# before transport = CavemanAsyncOpenAITransport(httpx2.HTTPTransport()) # TypeError: sync transport # after transport = CavemanAsyncOpenAITransport(httpx2.AsyncHTTPTransport())
Defensive patterns
Strategy: type-guard
Validate before calling
def ensure_async_transport(transport):
if not isinstance(transport, httpx2.AsyncBaseTransport):
raise TypeError("transport must be an httpx2.AsyncBaseTransport")
return transport Type guard
def is_async_transport(transport) -> bool:
return isinstance(transport, httpx2.AsyncBaseTransport) Try / catch
try:
transport = CavemanAsyncOpenAITransport(candidate)
except TypeError as e:
if "AsyncBaseTransport" in str(e):
candidate = httpx2.AsyncHTTPTransport()
transport = CavemanAsyncOpenAITransport(candidate) Prevention
- Never share a sync transport between sync and async clients
- Keep a single factory per client kind that constructs the right transport type
- Pin httpx2 versions in lockfiles to avoid attribute drift
- Type-annotate transport params as httpx2.AsyncBaseTransport
When it happens
Trigger: Constructing CavemanAsyncOpenAITransport with a sync httpx2.BaseTransport (like HTTPTransport), an httpx (v1) async transport, None, or any non-async transport object.
Common situations: Reusing the sync wrapper's transport in the async wrapper after copy-pasting setup code; mixing httpx/httpx2 after migration; passing httpx2.HTTPTransport where AsyncHTTPTransport is required.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Expected an HTTPX2 BaseTransport
- closed
- Match the sync/async Caveman transport to the native client
- Match the sync/async middleware runtime to the native client
- Use AsyncMiddlewareRuntime with the asynchronous transport
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/c7d0b602db24638e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/_httpx2.py:69
try:
response = self.transport.handle_request(request)
observe_response(response, attempt)
return response
except BaseException:
attempt.observe("failed")
raise
finally:
owner.reset(token)
def close(self):
self.transport.close()
class CavemanAsyncOpenAITransport(httpx2.AsyncBaseTransport):
"""Async counterpart of CavemanOpenAITransport, using the native pool."""
def __init__(self, transport):
if not isinstance(transport, httpx2.AsyncBaseTransport):
raise TypeError("Expected an HTTPX2 AsyncBaseTransport")
self.transport = transport
async def handle_async_request(self, request):
call = owner.get()
if not isinstance(call, OpenAICall) or call.transport is not self:
return await self.transport.handle_async_request(request)
attempt = call.next_attempt(request)
attempt.observe("dispatch_intent")
token = owner.set(attempt)
try:
response = await self.transport.handle_async_request(request)
observe_response(response, attempt)
return response
except (asyncio.CancelledError, GeneratorExit):
attempt.observe("cancelled")
raise
except BaseException:
attempt.observe("failed")View on GitHub (pinned to 3ee70a1026)