{"id":"fe5eba21443273fe","repo":"psycopg/psycopg2","slug":"dictcursorbase-can-t-be-instantiated-without-a-row","errorCode":null,"errorMessage":"DictCursorBase can't be instantiated without a row factory.","messagePattern":"DictCursorBase can't be instantiated without a row factory\\.","errorType":"exception","errorClass":"NotImplementedError","httpStatus":null,"severity":"error","filePath":"lib/extras.py","lineNumber":73,"sourceCode":"# Expose range-related objects\nfrom psycopg2._range import (                               # noqa\n    Range, NumericRange, DateRange, DateTimeRange, DateTimeTZRange,\n    register_range, RangeAdapter, RangeCaster)\n\n\n# Expose ipaddress-related objects\nfrom psycopg2._ipaddress import register_ipaddress          # noqa\n\n\nclass DictCursorBase(_cursor):\n    \"\"\"Base class for all dict-like cursors.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        if 'row_factory' in kwargs:\n            row_factory = kwargs['row_factory']\n            del kwargs['row_factory']\n        else:\n            raise NotImplementedError(\n                \"DictCursorBase can't be instantiated without a row factory.\")\n        super().__init__(*args, **kwargs)\n        self._query_executed = False\n        self._prefetch = False\n        self.row_factory = row_factory\n\n    def fetchone(self):\n        if self._prefetch:\n            res = super().fetchone()\n        if self._query_executed:\n            self._build_index()\n        if not self._prefetch:\n            res = super().fetchone()\n        return res\n\n    def fetchmany(self, size=None):\n        if self._prefetch:\n            res = super().fetchmany(size)","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/extras.py#L55-L91","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use the concrete classes DictCursor or RealDictCursor via the connection_factory or cursor_factory argument instead of DictCursorBase.","If subclassing DictCursorBase, pass row_factory=<callable> as a keyword to __init__ (the code pops it from kwargs at line 69-71).","Model your subclass on DictCursor, which sets its own tuple_factory and row_factory."],"exampleFix":"// before\ncur = conn.cursor(cursor_factory=DictCursorBase)\n// after\nfrom psycopg2.extras import DictCursor\ncur = conn.cursor(cursor_factory=DictCursor)","handlingStrategy":"type-guard","validationCode":"from psycopg2.extras import DictCursor, RealDictCursor  # use concrete classes\n# Never instantiate DictCursorBase directly; choose a concrete cursor.","typeGuard":"def is_usable_cursor_factory(cf) -> bool:\n    from psycopg2.extras import DictCursorBase\n    return isinstance(cf, type) and issubclass(cf, _cursor) and cf is not DictCursorBase","tryCatchPattern":"try:\n    cur = conn.cursor(cursor_factory=cf)\nexcept NotImplementedError:\n    cur = conn.cursor()  # fall back to default cursor","preventionTips":["Use DictCursor or RealDictCursor; treat DictCursorBase as abstract.","When subclassing DictCursorBase, always pass row_factory."],"tags":["cursor","abstract-class","not-implemented","dict-cursor","extras"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}