{"record":{"id":"88fdaa9c83c573bf","repo":"JuliusBrussee/caveman","slug":"synchronous-calls-require-middlewareruntime","errorCode":null,"errorMessage":"Synchronous calls require MiddlewareRuntime","messagePattern":"Synchronous calls require MiddlewareRuntime","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/langchain.py","lineNumber":215,"sourceCode":"        except (TypeError, ValueError, AttributeError, RecursionError):\n            return False\n\n    def binding(self, request, runtime):\n        if self.recovery_tool is None or runtime.mode != \"compress\" or owner.get() is not None:\n            return None\n        actual = [tool for tool in request.tools if getattr(tool, \"name\", None) == \"caveman_retrieve\" or (plain(tool) and tool.get(\"name\") == \"caveman_retrieve\")]\n        if len(actual) != 1 or actual[0] is not self.recovery_tool or not self._recovery_intact() or request.response_format is not None or request.tool_choice not in (None, \"auto\"):\n            return None\n        return runtime.recovery(_scope(self.connection.scope))\n\n    @property\n    def overhead(self):\n        return json.dumps({\"name\": \"caveman_retrieve\", \"description\": RECOVERY_DESCRIPTION, \"input_schema\": RECOVERY_SCHEMA}, ensure_ascii=False, separators=(\",\", \":\"))\n\n    def wrap_model_call(self, request, handler):\n        runtime = self.connection.sync\n        if runtime is None:\n            raise TypeError(\"Synchronous calls require MiddlewareRuntime\")\n        binding = self.binding(request, runtime)\n        messages, attempt = self.connection.prepare(request.messages, binding=binding, overhead=self.overhead if binding else None, prefix=[request.system_message] if request.system_message else [])\n        if attempt is None:\n            return handler(request)\n        attempt.observe(\"dispatch_intent\")\n        token = owner.set(attempt)\n        try:\n            response = handler(request.override(messages=messages))\n            _observe_messages(attempt, response)\n            return response\n        except BaseException:\n            attempt.observe(\"failed\")\n            raise\n        finally:\n            owner.reset(token)\n\n    async def awrap_model_call(self, request, handler):\n        runtime = self.connection.async_runtime","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/langchain.py#L197-L233","documentation":"Raised inside wrap_model_call in the LangChain middleware: a synchronous model call path was entered that requires MiddlewareRuntime, which is unavailable in this configuration. The middleware refuses to fake a synchronous compress/recover cycle without the engine-backed runtime.","triggerScenarios":"Registering the middleware as a synchronous LangChain agent middleware while the connection was created with only an AsyncMiddlewareRuntime (sync=None).","commonSituations":"Using LangGraph's sync AgentExecutor/stream with an adapter built for async; the same middleware object attached to both sync and async agents but configured with one runtime.","solutions":["Build the adapter/connection with a MiddlewareRuntime so sync is set.","Run the agent asynchronously (ainvoke) if only an async runtime exists.","Create separate adapter instances for sync and async runtimes rather than sharing one."],"exampleFix":"// before\nmiddleware = CavemanLangChainMiddleware(_Connection(async_runtime, scope))\nagent.invoke(input)  # wrap_model_call -> TypeError\n// after\nmiddleware = CavemanLangChainMiddleware(_Connection(MiddlewareRuntime(...), scope))\nagent.invoke(input)","handlingStrategy":"type-guard","validationCode":"if connection.sync is None:\n    raise RuntimeError('wrap_model_call (sync) requires MiddlewareRuntime; register only the async middleware')","typeGuard":"def has_sync_middleware(conn): return getattr(conn, 'sync', None) is not None","tryCatchPattern":"try:\n    out = agent.invoke(input)\nexcept TypeError as e:\n    if 'Synchronous calls require MiddlewareRuntime' in str(e):\n        raise RuntimeError('Use ainvoke with your async runtime, or build the adapter with MiddlewareRuntime') from e\n    raise","preventionTips":["Register sync middleware on sync agents and async middleware on async agents; never share one connection.","Validate at startup that connection.sync exists for every sync agent in the app.","Document which adapter constructors install sync vs async hooks."],"tags":["type-mismatch","middleware","sync-async","langchain"],"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"}