pola-rs/polars · error · TypeError

dtype '{dtype}' is a BaseExtension class, it should be an in

Error message

dtype '{dtype}' is a BaseExtension class, it should be an instance

What it means

Raised by polars.lit when the dtype argument is a BaseExtension subclass (an extension-dtype class) instead of an instance of it. Extension dtypes carry parameters (like the extension type they wrap), so polars must receive an instantiated dtype; classes are only accepted for built-in datatypes.

Source

Thrown at py-polars/src/polars/functions/lit.py:89

    >>> pl.lit(datetime(2023, 3, 31, 10, 30, 45))  # doctest: +IGNORE_RESULT

    Literal list/Series data (1D):

    >>> pl.lit([1, 2, 3])  # doctest: +SKIP
    >>> pl.lit(pl.Series("x", [1, 2, 3]))  # doctest: +IGNORE_RESULT

    Literal list/Series data (2D):

    >>> pl.lit([[1, 2], [3, 4]])  # doctest: +SKIP
    >>> pl.lit(pl.Series("y", [[1, 2], [3, 4]]))  # doctest: +IGNORE_RESULT
    """
    time_unit: TimeUnit

    if isinstance(dtype, BaseExtension):
        return lit(value, dtype.ext_storage()).ext.to(dtype)
    elif isinstance(dtype, type) and issubclass(dtype, BaseExtension):
        msg = f"dtype '{dtype}' is a BaseExtension class, it should be an instance"
        raise TypeError(msg)
    elif isinstance(dtype, DataTypeExpr):
        return lit(value).cast(dtype)
    elif dtype == Object:
        value_s = pl.Series("literal", [value], dtype=dtype)
        return wrap_expr(plr.lit(value_s._s, allow_object, is_scalar=True))

    if isinstance(value, datetime):
        if dtype == Date:
            return wrap_expr(plr.lit(value.date(), allow_object=False, is_scalar=True))

        # parse time unit
        if dtype is not None and (tu := getattr(dtype, "time_unit", "us")) is not None:
            tu = cast("TimeUnit", tu)
            time_unit = tu
        else:
            time_unit = "us"

        # parse time zone

View on GitHub (pinned to df599052da)

Solutions

  1. Instantiate the extension dtype: pl.lit(value, dtype=MyExtensionDType())
  2. If the extension wraps another storage type, you can also call pl.lit(value, dtype=MyExtensionDType().ext_storage()).ext.to(MyExtensionDType()) which is exactly what the instance path does
  3. Check for stray isinstance(dtype, type) in a wrapper/linter if dtype flows in from configuration

Example fix

// before
pl.lit(value, dtype=MyExtensionDType)
// after
pl.lit(value, dtype=MyExtensionDType())
Defensive patterns

Strategy: type-guard

Validate before calling

from polars.datatypes import BaseExtension

if isinstance(dtype, type) and issubclass(dtype, BaseExtension):
    dtype = dtype()  # instantiate before calling pl.lit

Type guard

def is_extension_dtype_instance(d) -> bool:
    return isinstance(d, BaseExtension)

Try / catch

try:
    e = pl.lit(value, dtype=dtype)
except TypeError:
    e = pl.lit(value, dtype=dtype())  # retry with an instance

Prevention

When it happens

Trigger: Writing pl.lit(value, dtype=MyExtensionDtype) (class, no parentheses) where MyExtensionDtype subclasses BaseExtension; passing the class when building a literal for a custom extension dtype registered via the plugin system.

Common situations: Authoring a polars extension-dtype plugin and mirroring the habitual pl.lit(x, dtype=pl.Int64) style where a class is fine; refactoring code that previously used only built-in dtypes; copy-pasting type annotations (classes) into runtime calls.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/89985e7be6cf9c6d. Report an issue: GitHub.