{"record":{"id":"99d46bfa5659ba5f","repo":"JuliusBrussee/caveman","slug":"match-the-sync-async-runtime-to-the-chat","errorCode":null,"errorMessage":"Match the sync/async runtime to the chat","messagePattern":"Match the sync/async runtime to the chat","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/google.py","lineNumber":185,"sourceCode":"        return client\n    _wrap_model(client.models, runtime, scope)\n    _wrap_model(client.aio.models, runtime.as_async(), scope, True)\n    return client\n\n\ndef with_caveman_google_chat(chat, *, runtime, scope, config=None):\n    \"\"\"Register recovery in an existing native Chat/AsyncChat's own AFC loop.\n\n    Pass the original default config here, or supply config on each send. No\n    private chat configuration/history is read or modified.\n    \"\"\"\n    asynchronous = isinstance(chat, AsyncChat)\n    if not isinstance(chat, (Chat, AsyncChat)):\n        raise TypeError(\"Expected a native Google Chat or AsyncChat\")\n    if asynchronous and isinstance(runtime, MiddlewareRuntime):\n        runtime = runtime.as_async()\n    if not isinstance(runtime, AsyncMiddlewareRuntime if asynchronous else MiddlewareRuntime):\n        raise TypeError(\"Match the sync/async runtime to the chat\")\n    if not supports_framework(runtime, (\"google-genai\", \"2.22\", \"3\")):\n        return chat\n    send, stream, default = chat.send_message, chat.send_message_stream, config\n    if asynchronous:\n        @functools.wraps(send)\n        async def send_message(message, config=None):\n            selected, context = _bind(config if config is not None else default, runtime, scope, asynchronous=True)\n            token = _invocation.set(context)\n            try:\n                return await send(message, config=selected)\n            finally:\n                _invocation.reset(token)\n\n        @functools.wraps(stream)\n        async def send_message_stream(message, config=None):\n            selected, context = _bind(config if config is not None else default, runtime, scope, asynchronous=True)\n            token = _invocation.set(context)\n            try:","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/google.py#L167-L203","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nasync_runtime = sync_runtime.as_async()\nwith_caveman_google_chat(sync_chat, async_runtime, scope)  # mismatch\n// after\nwith_caveman_google_chat(sync_chat, sync_runtime, scope)","handlingStrategy":"type-guard","validationCode":"from google.genai.chats import Chat, AsyncChat\nfrom caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\nexpected = AsyncMiddlewareRuntime if isinstance(chat, AsyncChat) else MiddlewareRuntime\nif not isinstance(runtime, expected):\n    raise TypeError(\"Runtime sync/async flavor must match the chat\")","typeGuard":"def runtime_matches_chat(chat, runtime) -> bool:\n    from google.genai.chats import Chat, AsyncChat\n    from caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\n    if isinstance(chat, AsyncChat):\n        return isinstance(runtime, (AsyncMiddlewareRuntime, MiddlewareRuntime))\n    return isinstance(runtime, MiddlewareRuntime) and not isinstance(runtime, AsyncMiddlewareRuntime)","tryCatchPattern":"try:\n    chat = with_caveman_google_chat(chat, runtime, scope)\nexcept TypeError as e:\n    if \"sync/async runtime\" in str(e):\n        runtime = sync_runtime.as_async() if isinstance(chat, AsyncChat) else sync_runtime\n        chat = with_caveman_google_chat(chat, runtime, scope)\n    else:\n        raise","preventionTips":["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."],"tags":["google","genai","chat","async","runtime"],"backgroundTag":"type-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}