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

  1. Pass the genai.Client itself: with_caveman_google(client, runtime, scope).
  2. Pass a synchronous MiddlewareRuntime; use runtime.as_async()/the async entrypoint for aio paths.
  3. 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

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


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)