psycopg/psycopg2 · error · NotImplementedError

DictCursorBase can't be instantiated without a row factory.

Error message

DictCursorBase can't be instantiated without a row factory.

What it means

Raised by DictCursorBase.__init__ (lib/extras.py:72-74) when a cursor is instantiated without a 'row_factory' keyword. DictCursorBase is an abstract base for DictCursor/RealDictCursor; concrete subclasses must supply a row_factory (a callable that builds each row). Instantiating the base class directly, or a subclass that fails to provide one, triggers this.

Source

Thrown at lib/extras.py:73

# Expose range-related objects
from psycopg2._range import (                               # noqa
    Range, NumericRange, DateRange, DateTimeRange, DateTimeTZRange,
    register_range, RangeAdapter, RangeCaster)


# Expose ipaddress-related objects
from psycopg2._ipaddress import register_ipaddress          # noqa


class DictCursorBase(_cursor):
    """Base class for all dict-like cursors."""

    def __init__(self, *args, **kwargs):
        if 'row_factory' in kwargs:
            row_factory = kwargs['row_factory']
            del kwargs['row_factory']
        else:
            raise NotImplementedError(
                "DictCursorBase can't be instantiated without a row factory.")
        super().__init__(*args, **kwargs)
        self._query_executed = False
        self._prefetch = False
        self.row_factory = row_factory

    def fetchone(self):
        if self._prefetch:
            res = super().fetchone()
        if self._query_executed:
            self._build_index()
        if not self._prefetch:
            res = super().fetchone()
        return res

    def fetchmany(self, size=None):
        if self._prefetch:
            res = super().fetchmany(size)

View on GitHub (pinned to 3a6d9d6ddc)

Solutions

  1. Use the concrete classes DictCursor or RealDictCursor via the connection_factory or cursor_factory argument instead of DictCursorBase.
  2. If subclassing DictCursorBase, pass row_factory=<callable> as a keyword to __init__ (the code pops it from kwargs at line 69-71).
  3. Model your subclass on DictCursor, which sets its own tuple_factory and row_factory.

Example fix

// before
cur = conn.cursor(cursor_factory=DictCursorBase)
// after
from psycopg2.extras import DictCursor
cur = conn.cursor(cursor_factory=DictCursor)
Defensive patterns

Strategy: type-guard

Validate before calling

from psycopg2.extras import DictCursor, RealDictCursor  # use concrete classes
# Never instantiate DictCursorBase directly; choose a concrete cursor.

Type guard

def is_usable_cursor_factory(cf) -> bool:
    from psycopg2.extras import DictCursorBase
    return isinstance(cf, type) and issubclass(cf, _cursor) and cf is not DictCursorBase

Try / catch

try:
    cur = conn.cursor(cursor_factory=cf)
except NotImplementedError:
    cur = conn.cursor()  # fall back to default cursor

Prevention

When it happens

Trigger: Calling DictCursorBase(...) directly, or defining a subclass without setting/ passing a row_factory. The normal subclasses (DictCursor, RealDictCursor) inject a row_factory internally, so this error indicates direct misuse or an incomplete subclass.

Common situations: A developer tries to build a custom dict-like cursor by subclassing DictCursorBase but forgets the row_factory. Also seen when someone instantiates DictCursorBase believing it is a usable cursor.

Related errors


AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04). Data as JSON: /data/errors/fe5eba21443273fe.json. Report an issue: GitHub.