{"record":{"id":"a70d6d46d3485836","repo":"JuliusBrussee/caveman","slug":"match-the-sync-async-middleware-runtime-to-the-native-client-a70d6d","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":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/openai.py","lineNumber":95,"sourceCode":"        raise ValueError(\"Every native function definition needs exactly one executor\")\n    if runtime.mode != \"compress\" or not in_range(__version__, \"3.10\", \"4\"):\n        return CavemanOpenAIToolLoop(with_caveman_openai(client, runtime=runtime, scope=scope, transport=transport), MappingProxyType(dict(functions)), json.dumps(definitions))\n    binding = runtime.recovery(scope)\n    tool = {\"name\": binding.name, \"description\": binding.description, \"parameters\": copy.deepcopy(binding.input_schema)}\n    definition = {\"type\": \"function\", \"function\": tool} if protocol == \"openai-chat\" else {\"type\": \"function\", **tool}\n    definitions.append(definition)\n    registry = MappingProxyType({**functions, binding.name: binding.execute})\n    registration = (protocol, binding, registry, registry[binding.name], json.dumps(definition, ensure_ascii=False, separators=(\",\", \":\")))\n    return CavemanOpenAIToolLoop(_wrap(client, runtime=runtime, scope=scope, registration=registration, transport=transport), registry,\n                                json.dumps(definitions, ensure_ascii=False, separators=(\",\", \":\")))\n\n\ndef _wrap(client, *, runtime, scope, registration=None, transport=None):\n    if not isinstance(client, (OpenAI, AsyncOpenAI)):\n        raise TypeError(\"Expected an OpenAI or AsyncOpenAI client\")\n    is_async = isinstance(client, AsyncOpenAI)\n    if not isinstance(runtime, AsyncMiddlewareRuntime if is_async else MiddlewareRuntime):\n        raise TypeError(\"Match the sync/async middleware runtime to the native client\")\n    if transport is not None and not isinstance(transport, CavemanAsyncOpenAITransport if is_async else CavemanOpenAITransport):\n        raise TypeError(\"Match the sync/async Caveman transport to the native client\")\n    version_supported = in_range(__version__, \"3.10\", \"4\")\n    if not version_supported and runtime.mode != \"off\":\n        runtime.decline(\"unsupported_version\")\n    native = client.with_options()\n    post = native.post\n    sessions = {}\n    for path, protocol in ((\"/chat/completions\", \"openai-chat\"), (\"/responses\", \"openai-responses\")):\n        bound = registration is not None and registration[0] == protocol\n        sessions[path] = NativeSession(runtime, scope, adapter_id=\"openai-sdk\", framework_version=\"3.10.0\", protocol=protocol,\n                                      binding=registration[1] if bound else None, overhead=registration[4] if bound else None,\n                                      is_registered=(lambda: registration[2].get(registration[1].name) is registration[3] and registration[1].execute is registration[3]) if bound else None,\n                                      passive_reason=None if version_supported else \"unsupported_version\")\n    passive_session = sessions[\"/chat/completions\"]\n\n    def session_for(path, kwargs):\n        if not isinstance(path, str) or not plain(kwargs.get(\"options\", {})) or kwargs.get(\"options\", {}).get(\"extra_json\") or kwargs.get(\"files\"):","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/openai.py#L77-L113","documentation":"The middleware runtime's sync/async flavor must match the client: AsyncOpenAI requires AsyncMiddlewareRuntime, sync OpenAI requires MiddlewareRuntime. Mismatched pairings raise this TypeError because the wrapper hooks sync vs async transports differently.","triggerScenarios":"Calling with_caveman_openai(AsyncOpenAI(...), runtime=MiddlewareRuntime(...)) or with_caveman_openai(OpenAI(...), runtime=AsyncMiddlewareRuntime(...)).","commonSituations":"Sharing one runtime between sync scripts and async apps; refactoring an app to asyncio and swapping the client but not the runtime; copy-pasted examples mixing variants.","solutions":["Pair AsyncOpenAI with AsyncMiddlewareRuntime and sync OpenAI with MiddlewareRuntime","Create a separate async runtime for the async client instead of reusing the sync one","Check factory helpers return the matching variant for your client type"],"exampleFix":"// before\nwith_caveman_openai(AsyncOpenAI(api_key=key), runtime=MiddlewareRuntime(mode=\"compress\"), scope=scope)\n// after\nwith_caveman_openai(AsyncOpenAI(api_key=key), runtime=AsyncMiddlewareRuntime(mode=\"compress\"), scope=scope)","handlingStrategy":"type-guard","validationCode":"from openai import OpenAI, AsyncOpenAI\nfrom caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\nif isinstance(client, AsyncOpenAI):\n    assert isinstance(runtime, AsyncMiddlewareRuntime)\nelse:\n    assert isinstance(runtime, MiddlewareRuntime)","typeGuard":"def runtime_matches_client(client, runtime):\n    from openai import OpenAI, AsyncOpenAI\n    from caveman_cloud.middleware import MiddlewareRuntime, AsyncMiddlewareRuntime\n    return isinstance(runtime, AsyncMiddlewareRuntime if isinstance(client, AsyncOpenAI) else MiddlewareRuntime)","tryCatchPattern":"try:\n    wrapped = with_caveman_openai(client, runtime=runtime, scope=scope)\nexcept TypeError as e:\n    if \"sync/async middleware runtime\" in str(e):\n        runtime = AsyncMiddlewareRuntime(mode=runtime.mode) if isinstance(client, AsyncOpenAI) else MiddlewareRuntime(mode=runtime.mode)\n        wrapped = with_caveman_openai(client, runtime=runtime, scope=scope)\n    else:\n        raise","preventionTips":["Pair client and runtime variants at construction time in one place","Never share a single runtime between sync and async entry points","Add an assertion helper used in every adapter wiring call"],"tags":["python","type-mismatch","openai","async"],"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"}