Repository: scikit-learn/scikit-learn

Issue:
# Attribute error from `sklearn.base.is_regressor` due to missing `__sklearn_tags__` on nightly builds

### Describe the bug

Running the following script works without a warning in sklearn 1.7, but fails on the latest nightly release. It appears to be related to https://github.com/scikit-learn/scikit-learn/pull/31134, but I'm unsure why the issue only became apparent now.

### Steps/Code to Reproduce

```python
from sklearn.base import is_regressor

class MyEstimator:
    def __init__(self):
        ...

    def fit(self, X, y=None):
        self.is_fitted_ = True
        return self

    def predict(self, X):
        ...


est = MyEstimator()
is_regressor(est)
```

### Expected Results

I would expect the code to continue to run, possibly with a `DeprecationWarning`.

### Actual Results

```
Traceback (most recent call last):
  File "/Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/python3.12/site-packages/sklearn/utils/_tags.py", line 275, in get_tags
    tags = estimator.__sklearn_tags__()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'MyEstimator' object has no attribute '__sklearn_tags__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/christian.bourjau/repos/scikit-learn-debug/t.py", line 17, in <module>
    is_regressor(est)
  File "/Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/python3.12/site-packages/sklearn/base.py", line 1238, in is_regressor
    return get_tags(estimator).estimator_type == "regressor"
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/python3.12/site-packages/sklearn/utils/_tags.py", line 283, in get_tags
    raise AttributeError(
AttributeError: The following error was raised: 'MyEstimator' object has no attribute '__sklearn_tags__'. It seems that there are no classes that implement `__sklearn_tags__` in the MRO and/or all classes in the MRO call `super().__sklearn_tags__()`. Make sure to inherit from `BaseEstimator` which implements `__sklearn_tags__` (or alternatively define `__sklearn_tags__` but we don't recommend this approach). Note that `BaseEstimator` needs to be on the right side of other Mixins in the inheritance order.
```

### Versions

```shell
System:
    python: 3.12.11 | packaged by conda-forge | (main, Jun  4 2025, 14:38:53) [Clang 18.1.8 ]
executable: /Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/bin/python
   machine: macOS-15.6.1-arm64-arm-64bit

Python dependencies:
      sklearn: 1.8.dev0
          pip: 25.2
   setuptools: 80.9.0
        numpy: 2.2.6
        scipy: 1.16.0
       Cython: 3.1.2
       pandas: 3.0.0.dev0+2502.g8476e0f756
   matplotlib: None
       joblib: 1.5.1
threadpoolctl: 3.6.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 16
         prefix: libopenblas
       filepath: /Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/libopenblas.0.dylib
        version: 0.3.30
threading_layer: openmp
   architecture: VORTEX

       user_api: openmp
   internal_api: openmp
    num_threads: 16
         prefix: libomp
       filepath: /Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/libomp.dylib
        version: None

       user_api: openmp
   internal_api: openmp
    num_threads: 16
         prefix: libomp
       filepath: /Users/christian.bourjau/repos/scikit-learn-debug/.pixi/envs/nightlies/lib/python3.12/site-packages/sklearn/.dylibs/libomp.dylib
        version: None
```

Implementation patch:
diff --git a/sklearn/utils/_tags.py b/sklearn/utils/_tags.py
index a87d34b4d54f3..5319fc692d449 100644
--- a/sklearn/utils/_tags.py
+++ b/sklearn/utils/_tags.py
@@ -271,6 +271,12 @@ def get_tags(estimator) -> Tags:
         The estimator tags.
     """
 
+    if isinstance(estimator, type):
+        raise TypeError(
+            f"Expected an estimator instance ({estimator.__name__}()), got "
+            f"estimator class instead ({estimator.__name__})."
+        )
+
     try:
         tags = estimator.__sklearn_tags__()
     except AttributeError as exc:


Test patch:
diff --git a/sklearn/utils/tests/test_tags.py b/sklearn/utils/tests/test_tags.py
index 5d910537b26d7..073b8359803c4 100644
--- a/sklearn/utils/tests/test_tags.py
+++ b/sklearn/utils/tests/test_tags.py
@@ -1,3 +1,4 @@
+import re
 from dataclasses import dataclass, fields
 
 import numpy as np
@@ -32,6 +33,21 @@ class EmptyRegressor(RegressorMixin, BaseEstimator):
     pass
 
 
+def test_type_error_is_thrown_for_class_vs_instance():
+    """Test that a clearer error is raised if a class is passed instead of an instance.
+
+    Related to the discussion in
+    https://github.com/scikit-learn/scikit-learn/issues/32394#issuecomment-3375647854.
+    """
+    estimator_class = EmptyClassifier
+    match = re.escape(
+        "Expected an estimator instance (EmptyClassifier()), "
+        "got estimator class instead (EmptyClassifier)."
+    )
+    with pytest.raises(TypeError, match=match):
+        get_tags(estimator_class)
+
+
 @pytest.mark.parametrize(
     "estimator, value",
     [