JuliusBrussee/caveman · error · TypeError
Expected a Google Client and synchronous MiddlewareRuntime
Error message
Expected a Google Client and synchronous MiddlewareRuntime
What it means
with_caveman_google instruments a google.genai.Client's models interfaces. It requires the first argument to be an actual genai.Client instance and the runtime to be a synchronous MiddlewareRuntime; anything else is rejected with this TypeError before any wrapping happens.
Solutions
- Pass the genai.Client itself: with_caveman_google(client, runtime, scope).
- Pass a synchronous MiddlewareRuntime; use runtime.as_async()/the async entrypoint for aio paths.
- Use with_caveman_google_chat if you actually have a Chat/AsyncChat rather than a Client.
Example fix
// before with_caveman_google(client.models, async_runtime, scope) // after with_caveman_google(client, sync_runtime, scope)
Defensive patterns
Strategy: type-guard
Validate before calling
from google import genai
from caveman_cloud.middleware import MiddlewareRuntime
if not isinstance(client, genai.Client) or not isinstance(runtime, MiddlewareRuntime):
raise TypeError("with_caveman_google requires genai.Client + sync MiddlewareRuntime") Type guard
def valid_google_args(client, runtime) -> bool:
from google import genai
from caveman_cloud.middleware import MiddlewareRuntime
return isinstance(client, genai.Client) and isinstance(runtime, MiddlewareRuntime) Try / catch
try:
client = with_caveman_google(client, runtime, scope)
except TypeError as e:
if "Google Client" in str(e):
raise ConfigError("Pass the genai.Client itself and a sync runtime") from e
raise Prevention
- Pass the Client object, never client.models or a chat.
- Keep separate sync/async runtimes and use the right one per entrypoint.
- Build Clients eagerly so you always wrap a real instance.
- Add argument assertions in your middleware bootstrap helper.
When it happens
Trigger: Passing a non-Client object (e.g. a Models interface, a Chat, a config dict, or None) as client, or an AsyncMiddlewareRuntime (or a plain dict/string) as runtime to with_caveman_google.
Common situations: Wrapping client.models instead of client; using the async runtime obtained for aio usage with the sync entrypoint; constructing the Client lazily and passing a factory/future; mixing up with_caveman_google and with_caveman_google_chat arguments.
Related errors
- Expected a native Google Chat or AsyncChat
- Match the sync/async runtime to the chat
- CrewAI requires a MiddlewareRuntime and a stable Scope per…
- Install caveman-middleware[google] to use the Google adapter
- cave_claude_terminal_${result?.subtype ?? "missing"}
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/9303ab5e802430fe.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/google.py:165
finally:
_invocation.reset(token)
@functools.wraps(stream)
def generate_content_stream(*, model, contents, config=None):
config, context = _bind(config, runtime, scope)
return _scope_iterator(stream(model=model, contents=list(contents) if type(contents) is list else contents, config=config), context)
module.generate_content, module.generate_content_stream = generate_content, generate_content_stream
def with_caveman_google(client, *, runtime, scope):
"""Register native model AFC on an existing, transport-configured Client.
This is instance-scoped registration on public methods. Construct the client
with the transports below; existing auth and client options remain native.
Use with_caveman_google_chat for a Chat, which owns a separate AFC loop.
"""
if not isinstance(client, genai.Client) or not isinstance(runtime, MiddlewareRuntime):
raise TypeError("Expected a Google Client and synchronous MiddlewareRuntime")
if not supports_framework(runtime, ("google-genai", "2.22", "3")):
return client
_wrap_model(client.models, runtime, scope)
_wrap_model(client.aio.models, runtime.as_async(), scope, True)
return client
def with_caveman_google_chat(chat, *, runtime, scope, config=None):
"""Register recovery in an existing native Chat/AsyncChat's own AFC loop.
Pass the original default config here, or supply config on each send. No
private chat configuration/history is read or modified.
"""
asynchronous = isinstance(chat, AsyncChat)
if not isinstance(chat, (Chat, AsyncChat)):
raise TypeError("Expected a native Google Chat or AsyncChat")
if asynchronous and isinstance(runtime, MiddlewareRuntime):
runtime = runtime.as_async()View on GitHub (pinned to 3ee70a1026)