JuliusBrussee/caveman · error · TypeError

Expected an Anthropic or AsyncAnthropic client

Error message

Expected an Anthropic or AsyncAnthropic client

What it means

with_caveman_anthropic clones a native Anthropic SDK client with caveman middleware attached. It validates that `client` is an Anthropic or AsyncAnthropic instance; anything else (a different provider client, a wrapper class, None) cannot be cloned, so TypeError is raised.

Solutions

  1. Construct an anthropic.Anthropic or anthropic.AsyncAnthropic client and pass that instance
  2. Check the import source — the client must come from the `anthropic` package installed with the extra
  3. Verify argument order/keywords: client first, then keyword-only runtime and scope

Example fix

// before
with_caveman_anthropic(openai_client, runtime=runtime, scope=scope)
// after
from anthropic import Anthropic
client = Anthropic()
with_caveman_anthropic(client, runtime=runtime, scope=scope)
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(client, (Anthropic, AsyncAnthropic)):
    raise TypeError('with_caveman_anthropic needs an Anthropic/AsyncAnthropic client')

Type guard

def is_anthropic_client(client) -> bool:
    from anthropic import Anthropic, AsyncAnthropic
    return isinstance(client, (Anthropic, AsyncAnthropic))

Try / catch

try:
    wrapped = with_caveman_anthropic(client, runtime=runtime, scope=scope)
except TypeError as e:
    if 'Anthropic' in str(e):
        wrapped = attach_generic_middleware(client, runtime, scope)
    else:
        raise

Prevention

When it happens

Trigger: Passing an OpenAI client, an anthropic Beta client, a subclass instance that failed isinstance, or None to with_caveman_anthropic(client=..., runtime=..., scope=...).

Common situations: Refactored code passing a generic client object; using a stubbed/mock client in tests; accidentally swapping client and runtime arguments.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/eaa95125f8fcda3d. Report an issue: GitHub.

Appendix: source

Thrown at packages/middleware/python/caveman_middleware/anthropic.py:114


@dataclass(frozen=True)
class _AsyncRecoveryTool(BetaAsyncBuiltinFunctionTool):
    binding: object
    definition: str
    execute: object

    def to_dict(self):
        return json.loads(self.definition)

    async def call(self, input):
        return json.dumps(await self.execute(input), ensure_ascii=False, separators=(",", ":"))


def with_caveman_anthropic(client, *, runtime, scope):
    """Return a native SDK clone. Existing native middleware stays in order."""
    if not isinstance(client, (Anthropic, AsyncAnthropic)):
        raise TypeError("Expected an Anthropic or AsyncAnthropic client")
    async_client = isinstance(client, AsyncAnthropic)
    if not isinstance(runtime, AsyncMiddlewareRuntime if async_client else MiddlewareRuntime):
        raise TypeError("Match the sync/async middleware runtime to the native client")
    version_supported = in_range(__version__, "1.4", "2")
    if not version_supported and runtime.mode != "off":
        runtime.decline("unsupported_version")
    native = client.with_middleware(CavemanAnthropicMiddleware(runtime, scope))
    if not version_supported or runtime.mode == "off":
        return native
    original_runner = native.beta.messages.tool_runner

    @functools.wraps(original_runner)
    def tool_runner(**params):
        supplied = params.get("tools")
        if runtime.mode != "compress" or not isinstance(supplied, (list, tuple)):
            return original_runner(**params)
        names = [(t.get("name") if plain(t) else getattr(t, "name", None)) for t in supplied]
        if any(type(name) is not str or not name for name in names) or len(set(names)) != len(names) or "caveman_retrieve" in names:

View on GitHub (pinned to 3ee70a1026)