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

  1. Instantiate MiddlewareRuntime (not AsyncMiddlewareRuntime) and pass it as runtime=.
  2. If you actually need async, use CavemanGoogleAsyncTransport instead.
  3. 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

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


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)