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
- 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';".
- Schema-qualify the name if it lives outside search_path: register_range('myschema.myrange', ...).
- Ensure the migration creating the range type (CREATE TYPE ... AS RANGE) has been applied on this database.
- 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
- Run the CREATE TYPE migration before registering range casters.
- Schema-qualify custom range type names to avoid search_path ambiguity.
- Verify role has USAGE on the schema hosting the range type.
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
- range types not available in version %s
- bound flags not valid: {bounds!r}
- RangeAdapter must be subclassed overriding its name or the g
- pgrange must be a string or a RangeAdapter strict subclass
- pyrange must be a type or a Range strict subclass
AI-assisted analysis of psycopg/psycopg2@3a6d9d6ddc (2026-08-04).
Data as JSON: /data/errors/c9af74057626796d.json.
Report an issue: GitHub.