{"record":{"id":"9ad0597bd5189f99","repo":"JuliusBrussee/caveman","slug":"rebind-cavemanllm-from-the-original-provider-configuration","errorCode":null,"errorMessage":"Rebind CavemanLLM from the original provider configuration and trusted runtime; active middleware is not serializable","messagePattern":"Rebind CavemanLLM from the original provider configuration and trusted runtime; active middleware is not serializable","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/crewai.py","lineNumber":185,"sourceCode":"        return self.delegate.supports_stop_words()\n\n    def get_context_window_size(self):\n        return self.delegate.get_context_window_size()\n\n    def supports_multimodal(self):\n        return self.delegate.supports_multimodal()\n\n    def format_text_content(self, text):\n        return self.delegate.format_text_content(text)\n\n    def get_file_uploader(self):\n        return self.delegate.get_file_uploader()\n\n    def get_token_usage_summary(self):\n        return self.delegate.get_token_usage_summary()\n\n    def to_config_dict(self):\n        raise ValueError(\"Rebind CavemanLLM from the original provider configuration and trusted runtime; active middleware is not serializable\")\n\n    def _options(self, messages, tools, available_functions, from_task, from_agent, response_model):\n        reference = self._context.get()\n        self._context.set(None)\n        executor = reference() if reference is not None else None\n        if owner.get() is not None:\n            return None\n        reason = (\"closed\" if self.closed else \"off\" if self.runtime.mode == \"off\" else\n                  \"unsupported_version\" if not matches_framework((\"crewai\", \"1.15\", \"2\")) else\n                  \"unsupported_shape\" if type(messages) is not list else None)\n        if reason:\n            return Attempt(self.runtime, self.scope, str(uuid.uuid4()), str(uuid.uuid4()),\n                           passive=True, reason=reason, adapter=\"crewai\"), {}, None\n        context = manifest(messages)\n        if context is None:\n            return Attempt(self.runtime, self.scope, str(uuid.uuid4()), str(uuid.uuid4()),\n                           passive=True, reason=\"unsupported_shape\", adapter=\"crewai\"), {}, None\n        bound = (executor is not None and executor.llm is self and executor.messages is messages","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/crewai.py#L167-L203","documentation":"to_config_dict serializes the LLM configuration (e.g. for CrewAI's config export/caching). An active CavemanLLM wraps a live runtime, bindings, and contextvars that cannot be serialized safely, so the method unconditionally raises ValueError directing you to re-create the wrapper from the original provider configuration instead.","triggerScenarios":"Calling to_config_dict() on a CavemanLLM instance — e.g. CrewAI (or user code) exporting agent/LLM config to a dict, saving crews, or round-tripping configuration through a cache.","commonSituations":"Persisting a configured crew to disk/JSON; CrewAI's internal config export paths encountering the wrapped LLM; attempting to pickle/serialize a crew containing the middleware-wrapped LLM.","solutions":["Serialize the ORIGINAL delegate LLM's config (llm.delegate.to_config_dict()) instead of the wrapper.","Keep the original provider configuration (model, api key env, params) and reconstruct CavemanLLM(delegate, runtime, scope) after deserialization.","Avoid exporting config from wrapped LLMs; wrap at load time, not at save time."],"exampleFix":"// before\ncfg = caveman_llm.to_config_dict()\n// after\ncfg = caveman_llm.delegate.to_config_dict()\n# later: caveman_llm = CavemanLLM(LLM(**cfg), runtime=runtime, scope=scope)","handlingStrategy":"try-catch","validationCode":"if hasattr(llm, \"delegate\"):\n    cfg = llm.delegate.to_config_dict()\nelse:\n    cfg = llm.to_config_dict()","typeGuard":"def serializable_llm(llm) -> bool:\n    return not hasattr(llm, \"delegate\")","tryCatchPattern":"try:\n    cfg = llm.to_config_dict()\nexcept ValueError:\n    cfg = llm.delegate.to_config_dict()  # serialize the native LLM instead","preventionTips":["Serialize the original provider LLM, then re-wrap after loading.","Never pickle/export crews containing CavemanLLM instances.","Keep the original provider config (model, params) alongside saved crews for re-wrapping.","Guard any config-export code path with a hasattr(llm, 'delegate') check."],"tags":["crewai","serialization","unsupported-operation"],"backgroundTag":"unsupported-operation","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"}