{"record":{"id":"4b968549fd5ef21f","repo":"JuliusBrussee/caveman","slug":"match-the-sync-async-middleware-runtime-to-the-native-client","errorCode":null,"errorMessage":"Match the sync/async middleware runtime to the native client","messagePattern":"Match the sync/async middleware runtime to the native client","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/anthropic.py","lineNumber":117,"sourceCode":"class _AsyncRecoveryTool(BetaAsyncBuiltinFunctionTool):\n    binding: object\n    definition: str\n    execute: object\n\n    def to_dict(self):\n        return json.loads(self.definition)\n\n    async def call(self, input):\n        return json.dumps(await self.execute(input), ensure_ascii=False, separators=(\",\", \":\"))\n\n\ndef with_caveman_anthropic(client, *, runtime, scope):\n    \"\"\"Return a native SDK clone. Existing native middleware stays in order.\"\"\"\n    if not isinstance(client, (Anthropic, AsyncAnthropic)):\n        raise TypeError(\"Expected an Anthropic or AsyncAnthropic client\")\n    async_client = isinstance(client, AsyncAnthropic)\n    if not isinstance(runtime, AsyncMiddlewareRuntime if async_client else MiddlewareRuntime):\n        raise TypeError(\"Match the sync/async middleware runtime to the native client\")\n    version_supported = in_range(__version__, \"1.4\", \"2\")\n    if not version_supported and runtime.mode != \"off\":\n        runtime.decline(\"unsupported_version\")\n    native = client.with_middleware(CavemanAnthropicMiddleware(runtime, scope))\n    if not version_supported or runtime.mode == \"off\":\n        return native\n    original_runner = native.beta.messages.tool_runner\n\n    @functools.wraps(original_runner)\n    def tool_runner(**params):\n        supplied = params.get(\"tools\")\n        if runtime.mode != \"compress\" or not isinstance(supplied, (list, tuple)):\n            return original_runner(**params)\n        names = [(t.get(\"name\") if plain(t) else getattr(t, \"name\", None)) for t in supplied]\n        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:\n            return original_runner(**params)\n        binding = runtime.recovery(scope)\n        definition = json.dumps({\"name\": binding.name, \"description\": binding.description, \"input_schema\": binding.input_schema}, ensure_ascii=False, separators=(\",\", \":\"))","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/anthropic.py#L99-L135","documentation":"After identifying the client as sync (Anthropic) or async (AsyncAnthropic), with_caveman_anthropic checks that the runtime kind matches: AsyncMiddlewareRuntime for async clients, MiddlewareRuntime for sync clients. Mismatched kinds would produce broken call semantics, so TypeError is raised.","triggerScenarios":"Calling with_caveman_anthropic(AsyncAnthropic(), runtime=MiddlewareRuntime(...)) or with_caveman_anthropic(Anthropic(), runtime=AsyncMiddlewareRuntime(...)).","commonSituations":"Reusing one shared runtime instance for both sync and async clients; copying configuration between a sync script and async service without swapping the runtime class.","solutions":["Match the runtime class to the client: MiddlewareRuntime for Anthropic, AsyncMiddlewareRuntime for AsyncAnthropic","Construct both a sync and async runtime if you serve both client kinds","Detect at the call site with isinstance(client, AsyncAnthropic) and pick the runtime accordingly"],"exampleFix":"// before\nwith_caveman_anthropic(AsyncAnthropic(), runtime=MiddlewareRuntime(...))  # TypeError\n// after\nfrom caveman_cloud.middleware import AsyncMiddlewareRuntime\nwith_caveman_anthropic(AsyncAnthropic(), runtime=AsyncMiddlewareRuntime(...), scope=scope)","handlingStrategy":"type-guard","validationCode":"from anthropic import AsyncAnthropic\nfrom caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\nexpected = AsyncMiddlewareRuntime if isinstance(client, AsyncAnthropic) else MiddlewareRuntime\nif not isinstance(runtime, expected):\n    raise TypeError('runtime kind must match client kind')","typeGuard":"def runtime_matches(client, runtime) -> bool:\n    from anthropic import AsyncAnthropic\n    from caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\n    return isinstance(runtime, AsyncMiddlewareRuntime if isinstance(client, AsyncAnthropic) else MiddlewareRuntime)","tryCatchPattern":"try:\n    wrapped = with_caveman_anthropic(client, runtime=runtime, scope=scope)\nexcept TypeError as e:\n    if 'Match the sync/async' in str(e):\n        wrapped = with_caveman_anthropic(client, runtime=rebuild_runtime_for(client), scope=scope)\n    else:\n        raise","preventionTips":["Create the runtime next to the client so kinds are chosen together","Add a helper that pairs client kind with runtime kind","Test both sync and async wiring paths"],"tags":["python","anthropic","sync-async","typeerror"],"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"}