{"id":"90f33bb2d82af08d","repo":"psycopg/psycopg2","slug":"pgrange-must-be-a-string-or-a-rangeadapter-strict","errorCode":null,"errorMessage":"pgrange must be a string or a RangeAdapter strict subclass","messagePattern":"pgrange must be a string or a RangeAdapter strict subclass","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"lib/_range.py","lineNumber":325,"sourceCode":"        \"\"\"Create Range and RangeAdapter classes if needed.\"\"\"\n        # if got a string create a new RangeAdapter concrete type (with a name)\n        # else take it as an adapter. Passing an adapter should be considered\n        # an implementation detail and is not documented. It is currently used\n        # for the numeric ranges.\n        self.adapter = None\n        if isinstance(pgrange, str):\n            self.adapter = type(pgrange, (RangeAdapter,), {})\n            self.adapter.name = pgrange\n        else:\n            try:\n                if issubclass(pgrange, RangeAdapter) \\\n                        and pgrange is not RangeAdapter:\n                    self.adapter = pgrange\n            except TypeError:\n                pass\n\n        if self.adapter is None:\n            raise TypeError(\n                'pgrange must be a string or a RangeAdapter strict subclass')\n\n        self.range = None\n        try:\n            if isinstance(pyrange, str):\n                self.range = type(pyrange, (Range,), {})\n            if issubclass(pyrange, Range) and pyrange is not Range:\n                self.range = pyrange\n        except TypeError:\n            pass\n\n        if self.range is None:\n            raise TypeError(\n                'pyrange must be a type or a Range strict subclass')\n\n    @classmethod\n    def _from_db(self, name, pyrange, conn_or_curs):\n        \"\"\"Return a `RangeCaster` instance for the type *pgrange*.","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/psycopg/psycopg2/blob/3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db/lib/_range.py#L307-L343","documentation":"Raised by RangeCaster._create_ranges() (lib/_range.py:324-326) when the 'pgrange' argument is neither a string nor a strict subclass of RangeAdapter (i.e. it is RangeAdapter itself, a non-RangeAdapter class, or a non-type value). The constructor uses pgrange to build the adapter that serializes Python Range objects to PostgreSQL range literals, so it must resolve to a concrete adapter.","triggerScenarios":"Constructing RangeCaster directly with an invalid first argument: RangeCaster(123, MyRange, ...), RangeCaster(RangeAdapter, MyRange, ...) (the base class is explicitly excluded at line 319), or RangeCaster(SomeOtherClass, MyRange, ...). register_range() forwards its first arg to RangeCaster._from_db which always passes a string, so this is mainly a direct-construction issue.","commonSituations":"Developers bypass register_range() and instantiate RangeCaster manually (the docstring at line 288-291 allows this) but pass the wrong type for pgrange, confusing it with the Python range class.","solutions":["Pass a string (the PostgreSQL range type name, optionally schema-qualified) as the first argument to RangeCaster.","Or pass a strict subclass of RangeAdapter that has a 'name' set.","Prefer register_range(pgrange_name, pyrange, conn) which handles adapter creation correctly."],"exampleFix":"// before\ncaster = RangeCaster(my_adapter_base, MyRange, oid=123, subtype_oid=23)\n// after\ncaster = RangeCaster('myrange', MyRange, oid=123, subtype_oid=23)","handlingStrategy":"type-guard","validationCode":"if not (isinstance(pgrange, str)\n        or (isinstance(pgrange, type) and issubclass(pgrange, RangeAdapter)\n            and pgrange is not RangeAdapter)):\n    raise TypeError('pgrange must be str or a strict RangeAdapter subclass')","typeGuard":"def is_valid_pgrange(p) -> bool:\n    return (isinstance(p, str)\n            or (isinstance(p, type) and issubclass(p, RangeAdapter)\n                and p is not RangeAdapter))","tryCatchPattern":"try:\n    caster = RangeCaster(pgrange, pyrange, oid=..., subtype_oid=...)\nexcept TypeError:\n    # fall back to passing the type name as a string\n    caster = RangeCaster(str(pgrange), pyrange, oid=..., subtype_oid=...)","preventionTips":["Pass the PostgreSQL type name (string) unless you have a custom adapter class.","Use register_range() which normalizes this argument for you."],"tags":["type-error","range","rangecaster","constructor","adapter"],"analyzedSha":"3a6d9d6ddc6b53eaa80b712f5fa6b23abbdc38db","analyzedAt":"2026-08-04T19:56:51.958Z","schemaVersion":2}