{"record":{"id":"06a1a50ff242ff6e","repo":"JuliusBrussee/caveman","slug":"expected-an-existing-native-pydantic-ai-model","errorCode":null,"errorMessage":"Expected an existing native Pydantic AI Model","messagePattern":"Expected an existing native Pydantic AI Model","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/pydantic_ai.py","lineNumber":196,"sourceCode":"                and actual.args_validator_func is self.recovery_tool.args_validator\n                and len(offered) == 1):\n            return False\n        definition = offered[0]\n        return (definition == actual.tool_def and definition.kind == \"function\" and not definition.defer_loading\n                and definition.parameters_json_schema == RECOVERY_SCHEMA and definition.description == RECOVERY_DESCRIPTION\n                and parameters.visibility_of(definition.name) == \"visible\")\n\n    async def wrap_model_request(self, ctx: RunContext, *, request_context: ModelRequestContext, handler):\n        model = CavemanModel(request_context.model, runtime=self.runtime, scope=self.scope_source,\n                             registration=self, run_context=ctx)\n        return await handler(replace(request_context, model=model))\n\n\nclass CavemanModel(WrapperModel):\n    \"\"\"Native Model delegate; direct model-only use has no recovery executor.\"\"\"\n    def __init__(self, wrapped: Model, *, runtime, scope, registration=None, run_context=None):\n        if not isinstance(wrapped, Model):\n            raise TypeError(\"Expected an existing native Pydantic AI Model\")\n        super().__init__(wrapped)\n        self.runtime, self.scope_source = _runtime(runtime), scope\n        self.version_supported = _check_version(runtime)\n        self.registration, self.run_context = registration, run_context\n\n    async def _prepare(self, messages, settings, parameters):\n        if owner.get() is not None:\n            return messages, None\n        protocol = _protocol(self.wrapped)\n        def passive(reason):\n            return messages, Attempt(self.runtime, None, str(uuid.uuid4()), str(uuid.uuid4()),\n                                     passive=True, reason=reason, adapter=ADAPTER.id)\n        if self.runtime.mode == \"off\":\n            return passive(\"disabled\")\n        if not self.version_supported or protocol is None:\n            return passive(\"unsupported_version\" if not self.version_supported else \"unsupported_provider\")\n        selected = _message_view(messages)\n        if selected is None:","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/pydantic_ai.py#L178-L214","documentation":"CavemanModel is a WrapperModel that delegates to an existing native Pydantic AI Model. The constructor type-checks the wrapped argument; passing a non-Model object (a string model name, a provider client, None) is rejected with a TypeError because wrapping would fail later anyway.","triggerScenarios":"Calling CavemanModel(\"openai:gpt-4o\", runtime=..., scope=...) with a model identifier string instead of an instantiated Model; passing a provider client object or None obtained from a failed factory.","commonSituations":"Confusing Pydantic AI's string model names with Model instances; refactoring code that previously resolved model names lazily; fixtures passing mocks that are not registered as Model subclasses.","solutions":["Instantiate the model first, e.g. CavemanModel(OpenAIChatModel(\"gpt-4o\"), runtime=..., scope=...) or wrap an existing model instance you already use","Resolve string model names via Pydantic AI's model factory before wrapping","Check the value with isinstance(x, Model) before constructing CavemanModel"],"exampleFix":"// before\nmodel = CavemanModel(\"openai:gpt-4o\", runtime=rt, scope=scope)\n// after\nfrom pydantic_ai.models.openai import OpenAIChatModel\nmodel = CavemanModel(OpenAIChatModel(\"gpt-4o\"), runtime=rt, scope=scope)","handlingStrategy":"type-guard","validationCode":"from pydantic_ai.models import Model\nassert isinstance(wrapped, Model), \"CavemanModel needs a native pydantic_ai Model instance\"","typeGuard":"def is_native_model(x) -> bool:\n    return isinstance(x, Model)","tryCatchPattern":"try:\n    model = CavemanModel(wrapped, runtime=rt, scope=scope)\nexcept TypeError as e:\n    if \"native Pydantic AI Model\" in str(e):\n        wrapped = resolve_model_by_name(str(wrapped))\n        model = CavemanModel(wrapped, runtime=rt, scope=scope)\n    else:\n        raise","preventionTips":["Instantiate the model via Pydantic AI's provider classes before wrapping","Never pass model name strings, provider clients, or None to CavemanModel","Check isinstance(x, Model) at factory boundaries","In tests, use real Model instances or registered subclasses, not bare mocks"],"tags":["python","pydantic-ai","constructor","type-error"],"backgroundTag":"invalid-constructor-argument","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"}