psycopg/psycopg2 · error · ProgrammingError

PostgreSQL range '{name}' not found

Error message

PostgreSQL range '{name}' not found

What it means

Raised by RangeCaster._from_db() (lib/_range.py:409-411) after both lookup strategies against pg_range/pg_type fail to find a range type with the given name in the given schema. The first query filters by typname and namespace; a fallback (lib/_range.py:388-400) uses '%s::regtype' to respect search_path. If neither returns a row, the type does not exist or is not visible.

Source

Thrown at lib/_range.py:410

join pg_namespace ns on ns.oid = typnamespace
WHERE t.oid = %s::regtype
""", (name, ))
            except ProgrammingError:
                pass
            else:
                rec = curs.fetchone()
                if rec:
                    tname, schema = rec[3:]
            finally:
                if savepoint:
                    curs.execute("ROLLBACK TO SAVEPOINT register_type")

        # revert the status of the connection as before the command
        if conn_status != STATUS_IN_TRANSACTION and not conn.autocommit:
            conn.rollback()

        if not rec:
            raise ProgrammingError(
                f"PostgreSQL range '{name}' not found")

        type, subtype, array = rec[:3]

        return RangeCaster(name, pyrange,
            oid=type, subtype_oid=subtype, array_oid=array)

    _re_range = re.compile(r"""
        ( \(|\[ )                   # lower bound flag
        (?:                         # lower bound:
          " ( (?: [^"] | "")* ) "   #   - a quoted string
          | ( [^",]+ )              #   - or an unquoted string
        )?                          #   - or empty (not catched)
        ,
        (?:                         # upper bound:
          " ( (?: [^"] | "")* ) "   #   - a quoted string
          | ( [^"\)\]]+ )           #   - or an unquoted string
        )?                          #   - or empty (not catched)

View on GitHub (pinned to 3a6d9d6ddc)

Solutions

  1. Verify the type exists and is a range: run "SELECT rngtypid FROM pg_range JOIN pg_type t ON t.oid=rngtypid WHERE typname='X';".
  2. Schema-qualify the name if it lives outside search_path: register_range('myschema.myrange', ...).
  3. Ensure the migration creating the range type (CREATE TYPE ... AS RANGE) has been applied on this database.
  4. Check that the connecting role has USAGE privilege on the owning schema.

Example fix

// before
register_range('myrang', MyRange, conn)  # typo
// after
register_range('appschema.myrange', MyRange, conn)
Defensive patterns

Strategy: validation

Validate before calling

with conn.cursor() as c:
    c.execute("SELECT 1 FROM pg_range r JOIN pg_type t ON t.oid=r.rngtypid "
              "JOIN pg_namespace n ON n.oid=t.typnamespace "
              "WHERE t.typname=%s", (name.split('.')[-1],))
    if c.fetchone() is None:
        raise ValueError(f'range type {name!r} does not exist')

Type guard

def range_type_exists(conn, name: str) -> bool:
    with conn.cursor() as c:
        c.execute("SELECT EXISTS(SELECT 1 FROM pg_range r JOIN pg_type t "
                  "ON t.oid=r.rngtypid WHERE t.typname=%s)", (name.split('.')[-1],))
        return bool(c.fetchone()[0])

Try / catch

try:
    caster = register_range(name, pyrange, conn)
except ProgrammingError as e:
    if 'not found' in str(e):
        # create the type or skip
        pass
    else: raise

Prevention

When it happens

Trigger: Calling register_range('nonexistent', pyrange, conn), or passing a schema-qualified name where the schema or type is misspelled, or registering a type that is a plain type (not a range), or a range defined in a schema not on the connection's search_path and not schema-qualified correctly.

Common situations: Typos in the type name, wrong schema qualification, connecting as a different role that lacks USAGE privilege on the schema, or assuming a custom range type exists when the migration that creates it hasn't run.

Related errors


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