{"record":{"id":"030db30f35027ca1","repo":"JuliusBrussee/caveman","slug":"synchronous-document-compression-requires-middlewareruntime","errorCode":null,"errorMessage":"Synchronous document compression requires MiddlewareRuntime","messagePattern":"Synchronous document compression requires MiddlewareRuntime","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/langchain.py","lineNumber":397,"sourceCode":"            self.runtime.report(reason=\"off\" if self.runtime.mode == \"off\" else \"unsupported_version\", adapter=\"langchain-rag\")\n            return None\n        if any(type(document) is not Document for document in documents):\n            self.runtime.report(reason=\"unsupported_shape\", adapter=\"langchain-rag\")\n            return None\n        context = manifest([{\"id\": d.id, \"metadata\": d.metadata, \"page_content\": d.page_content} for d in documents])\n        if context is None:\n            self.runtime.report(reason=\"unsupported_shape\", adapter=\"langchain-rag\")\n            return None\n        scope = _scope(self.scope)\n        reader = self.source_expansion\n        binding = reader if (isinstance(reader, RecoveryBinding) and callable(reader.execute)\n                             and self.runtime.owns_binding(reader, scope)) else None\n        return dict(scope=scope, adapter=Adapter(\"langchain-rag\", \"0.1.0\", \"1.4.0\", \"langchain-document-v1\"), manifest=context,\n                    candidates=[Candidate(f\"document-{i}\", d.page_content, d.id or f\"document-{i}\", kind=\"artifact\") for i, d in enumerate(documents)], binding=binding)\n\n    def _compress(self, documents, query):\n        if not isinstance(self.runtime, MiddlewareRuntime):\n            raise TypeError(\"Synchronous document compression requires MiddlewareRuntime\")\n        documents = list(documents)\n        options = self._options(documents, query)\n        return documents if options is None else self._apply_documents(documents, self.runtime.optimize(**options))\n\n    def _apply_documents(self, documents, outcome):\n        replacements = {r[\"segment_id\"]: r[\"text\"] for r in outcome.replacements}\n        if not replacements.keys() <= {f\"document-{i}\" for i in range(len(documents))}:\n            self.runtime.report(reason=\"invalid_replacement_plan\", adapter=\"langchain-rag\")\n            return documents\n        result = [document.model_copy(update={\"page_content\": replacements[f\"document-{i}\"]}) if f\"document-{i}\" in replacements else document for i, document in enumerate(documents)]\n        self.runtime.report(outcome, adapter=\"langchain-rag\")\n        return result\n","sourceCodeStart":379,"sourceCodeEnd":410,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/langchain.py#L379-L410","documentation":"_compress is the synchronous document-compressor path and requires self.runtime to be a MiddlewareRuntime. An async runtime (AsyncMiddlewareRuntime) or None cannot run the sync optimize() call, so a TypeError is raised before computing options.","triggerScenarios":"Calling compress_documents (sync) on a compressor constructed with an AsyncMiddlewareRuntime or with no runtime.","commonSituations":"Using the async retriever/compressor with a sync VectorStore.get_relevant_documents pipeline; RAG chain refactor swapped the runtime type but kept the sync compressor.","solutions":["Construct the compressor with a MiddlewareRuntime.","Use the async compression path (acompress_documents) when only an async runtime is available.","If compression is intentionally disabled, pass documents through without the compressor."],"exampleFix":"// before\ncompressor = CavemanDocumentCompressor(async_runtime, scope)\ncompressor.compress_documents(docs, query)  # TypeError\n// after\ncompressor = CavemanDocumentCompressor(MiddlewareRuntime(...), scope)\ncompressor.compress_documents(docs, query)","handlingStrategy":"type-guard","validationCode":"if not isinstance(compressor.runtime, MiddlewareRuntime):\n    raise RuntimeError('sync compression requires MiddlewareRuntime; use acompress_documents otherwise')","typeGuard":"def supports_sync_compression(c): return isinstance(getattr(c, 'runtime', None), MiddlewareRuntime)","tryCatchPattern":"try:\n    docs = compressor.compress_documents(documents, query)\nexcept TypeError as e:\n    if 'Synchronous document compression' in str(e):\n        docs = await compressor.acompress_documents(documents, query)\n    else:\n        raise","preventionTips":["Match compressor runtime type to the retriever's sync/async flavor.","In async RAG chains, always call acompress_documents.","Validate the runtime type at compressor construction, not at first use."],"tags":["type-mismatch","rag","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"}