psycopg/psycopg2 · error · ProgrammingError

range types not available in version %s

Error message

range types not available in version %s

What it means

Raised by RangeCaster._from_db() (lib/_range.py:351-353) when the connected PostgreSQL server reports a version below 9.2 (server_version < 90200). Range types (int4range, daterange, tsrange, etc.) were introduced in PostgreSQL 9.2, so querying pg_range on an older server is meaningless and the function refuses to proceed.

Source

Thrown at lib/_range.py:352

        except TypeError:
            pass

        if self.range is None:
            raise TypeError(
                'pyrange must be a type or a Range strict subclass')

    @classmethod
    def _from_db(self, name, pyrange, conn_or_curs):
        """Return a `RangeCaster` instance for the type *pgrange*.

        Raise `ProgrammingError` if the type is not found.
        """
        from psycopg2.extensions import STATUS_IN_TRANSACTION
        from psycopg2.extras import _solve_conn_curs
        conn, curs = _solve_conn_curs(conn_or_curs)

        if conn.info.server_version < 90200:
            raise ProgrammingError("range types not available in version %s"
                % conn.info.server_version)

        # Store the transaction status of the connection to revert it after use
        conn_status = conn.status

        # Use the correct schema
        if '.' in name:
            schema, tname = name.split('.', 1)
        else:
            tname = name
            schema = 'public'

        # get the type oid and attributes
        curs.execute("""\
select rngtypid, rngsubtype, typarray
from pg_range r
join pg_type t on t.oid = rngtypid
join pg_namespace ns on ns.oid = typnamespace

View on GitHub (pinned to 3a6d9d6ddc)

Solutions

  1. Upgrade the PostgreSQL server to 9.2 or newer (9.2 is itself ancient; prefer a supported major version).
  2. Verify the connection target with 'SELECT version();' and fix the DSN/host if it points to the wrong server.
  3. If upgrade is impossible, avoid the range API entirely and model the data with separate columns.

Example fix

// before
register_range('int4range', NumericRange, old_conn)  # server is 9.1
// after
# point at an upgraded server (9.2+) then:
register_range('int4range', NumericRange, new_conn)
Defensive patterns

Strategy: validation

Validate before calling

if conn.info.server_version < 90200:
    raise RuntimeError(f'range types require PostgreSQL >= 9.2, got {conn.info.server_version}')

Type guard

def server_supports_ranges(conn) -> bool:
    return conn.info.server_version >= 90200

Try / catch

try:
    caster = register_range(name, pyrange, conn)
except ProgrammingError as e:
    if 'range types not available' in str(e):
        # fall back to non-range representation
        pass
    else: raise

Prevention

When it happens

Trigger: Calling register_range(pgrange, pyrange, conn_or_curs) or otherwise invoking RangeCaster._from_db() against a database whose server version is < 9.2. The check reads conn.info.server_version.

Common situations: Connecting to a legacy PostgreSQL deployment (8.x, 9.0, 9.1) still common in older on-premise or appliance environments. Also seen when a connection string accidentally points to a standby/replica running an older major version, or after a botched upgrade.

Related errors


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