JuliusBrussee/caveman · error · TypeError
Match the sync/async runtime to the chat
Error message
Match the sync/async runtime to the chat
What it means
with_caveman_google_chat requires the runtime kind to match the chat flavor: sync MiddlewareRuntime for Chat, AsyncMiddlewareRuntime for AsyncChat. It auto-converts a sync runtime when the chat is async, but if after that conversion the runtime still does not match the chat's sync/async flavor, this TypeError is raised.
Solutions
- For a sync Chat, pass a MiddlewareRuntime; for an AsyncChat, pass AsyncMiddlewareRuntime or a sync runtime (auto-converted).
- Convert explicitly with runtime.as_async() when pairing with AsyncChat.
- Construct a proper sync MiddlewareRuntime instead of reusing the async one for sync chats.
Example fix
// before async_runtime = sync_runtime.as_async() with_caveman_google_chat(sync_chat, async_runtime, scope) # mismatch // after with_caveman_google_chat(sync_chat, sync_runtime, scope)
Defensive patterns
Strategy: type-guard
Validate before calling
from google.genai.chats import Chat, AsyncChat
from caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime
expected = AsyncMiddlewareRuntime if isinstance(chat, AsyncChat) else MiddlewareRuntime
if not isinstance(runtime, expected):
raise TypeError("Runtime sync/async flavor must match the chat") Type guard
def runtime_matches_chat(chat, runtime) -> bool:
from google.genai.chats import Chat, AsyncChat
from caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime
if isinstance(chat, AsyncChat):
return isinstance(runtime, (AsyncMiddlewareRuntime, MiddlewareRuntime))
return isinstance(runtime, MiddlewareRuntime) and not isinstance(runtime, AsyncMiddlewareRuntime) Try / catch
try:
chat = with_caveman_google_chat(chat, runtime, scope)
except TypeError as e:
if "sync/async runtime" in str(e):
runtime = sync_runtime.as_async() if isinstance(chat, AsyncChat) else sync_runtime
chat = with_caveman_google_chat(chat, runtime, scope)
else:
raise Prevention
- Keep dedicated sync and async runtimes; never share the async one with sync chats.
- Call runtime.as_async() explicitly when targeting AsyncChat.
- Always construct MiddlewareRuntime instances (never dicts/strings) for the Google adapter.
- Type-check the runtime/chat pairing in a shared bootstrap helper.
When it happens
Trigger: Passing an AsyncMiddlewareRuntime with a synchronous Chat (auto-conversion only goes sync->async), or a runtime object that is neither MiddlewareRuntime nor AsyncMiddlewareRuntime (e.g. a dict/string/None).
Common situations: Reusing the async runtime (created for client.aio) with a synchronous chat; passing a plain dict config as runtime; a custom runtime class that doesn't subclass either expected runtime type.
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 a Google Client and synchronous MiddlewareRuntime
- Expected a native Google Chat or AsyncChat
- Install caveman-middleware[google] to use the Google adapter
- cave_claude_terminal_${result?.subtype ?? "missing"}
- cave_conversation_invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/99d46bfa5659ba5f.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/python/caveman_middleware/google.py:185
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()
if not isinstance(runtime, AsyncMiddlewareRuntime if asynchronous else MiddlewareRuntime):
raise TypeError("Match the sync/async runtime to the chat")
if not supports_framework(runtime, ("google-genai", "2.22", "3")):
return chat
send, stream, default = chat.send_message, chat.send_message_stream, config
if asynchronous:
@functools.wraps(send)
async def send_message(message, config=None):
selected, context = _bind(config if config is not None else default, runtime, scope, asynchronous=True)
token = _invocation.set(context)
try:
return await send(message, config=selected)
finally:
_invocation.reset(token)
@functools.wraps(stream)
async def send_message_stream(message, config=None):
selected, context = _bind(config if config is not None else default, runtime, scope, asynchronous=True)
token = _invocation.set(context)
try:View on GitHub (pinned to 3ee70a1026)