JuliusBrussee/caveman · error · TypeError
Use MiddlewareRuntime with the synchronous transport
Error message
Use MiddlewareRuntime with the synchronous transport
What it means
CavemanGoogleTransport is the synchronous HTTPX transport and only accepts a MiddlewareRuntime (sync) instance. Passing anything else — including AsyncMiddlewareRuntime, None, or a foreign object — triggers this TypeError at construction. It enforces that sync transports never run an async runtime's bounded execution path.
Solutions
- Instantiate MiddlewareRuntime (not AsyncMiddlewareRuntime) and pass it as runtime=.
- If you actually need async, use CavemanGoogleAsyncTransport instead.
- Check for swapped/positional arguments so scope is not landing in runtime.
Example fix
// before transport = CavemanGoogleTransport(runtime=runtime.as_async(), scope=scope) // after transport = CavemanGoogleTransport(runtime=MiddlewareRuntime(...), scope=scope)
Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(runtime, MiddlewareRuntime):
raise ValueError('CavemanGoogleTransport needs a sync MiddlewareRuntime')
transport = CavemanGoogleTransport(runtime=runtime, scope=scope) Type guard
def is_sync_runtime(r): return isinstance(r, MiddlewareRuntime)
Try / catch
try:
transport = CavemanGoogleTransport(runtime=runtime, scope=scope)
except TypeError:
transport = CavemanGoogleTransport(runtime=runtime.as_sync() if hasattr(runtime, 'as_sync') else MiddlewareRuntime(...), scope=scope) Prevention
- Keep sync and async runtime construction in separate, clearly named factory functions.
- Type-annotate runtime parameters as MiddlewareRuntime so static checkers flag misuse.
- Never pass runtimes positionally; always use runtime= keyword.
When it happens
Trigger: Calling CavemanGoogleTransport(runtime=<not a MiddlewareRuntime>, scope=...) — e.g. passing an AsyncMiddlewareRuntime, a raw dict/config object, or forgetting the keyword and passing scope positionally into runtime.
Common situations: Copy-pasting the async transport setup into sync code; swapping transports after refactoring to async; constructing the transport from an unparsed config value that is not a runtime instance.
Related errors
- Use AsyncMiddlewareRuntime with the asynchronous transport
- Native MCP clients require AsyncMiddlewareRuntime
- ASGI context must come from authenticated server state
- Expected a native LangChain BaseChatModel
- Expected a native Strands Model
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/839f99eab84b79f2.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/google.py:426
self.finish("completed")
except BaseException:
self.finish("cancelled")
raise
finally:
await self.aclose()
async def aclose(self):
try:
await self.stream.aclose()
finally:
self.finish("cancelled")
class CavemanGoogleTransport(httpx.BaseTransport):
"""Public HTTPX transport; original-content request hooks run before it."""
def __init__(self, *, runtime, scope, provider_base_url="https://generativelanguage.googleapis.com", transport=None):
if not isinstance(runtime, MiddlewareRuntime):
raise TypeError("Use MiddlewareRuntime with the synchronous transport")
self.runtime, self.scope, self.transport = runtime, scope, transport or httpx.HTTPTransport()
self.base = urlsplit(provider_base_url)
def handle_request(self, request):
url = urlsplit(str(request.url))
if (url.scheme, url.netloc) != (self.base.scheme, self.base.netloc) or not url.path.startswith(self.base.path.rstrip("/") + "/"):
return self.transport.handle_request(request)
state = _prepare(request, self.runtime, self.scope)
if state is None:
return self.transport.handle_request(request)
attempt, plan = state
outgoing = _apply(request, state, self.runtime.optimize(**plan[2]) if plan else None)
attempt.observe("dispatch_intent")
token = owner.set(attempt)
try:
response = self.transport.handle_request(outgoing)
if response.status_code >= 400:
attempt.observe("failed")View on GitHub (pinned to 3ee70a1026)