jackc/pgx · error

multirange element OID not registered

Error message

multirange element OID not registered

What it means

Returned by Conn.LoadType for a typtype='m' (multirange) type when its range element OID is not in the connection's TypeMap (conn.go:1357-1359). A multirange codec wraps the same element type as its underlying range; if that element is unregistered the multirange cannot be built.

Source

Thrown at conn.go:1359

		if err != nil {
			return nil, err
		}

		dt, ok := c.TypeMap().TypeForOID(elementOID)
		if !ok {
			return nil, errors.New("range element OID not registered")
		}

		return &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.RangeCodec{ElementType: dt}}, nil
	case "m": // multirange
		elementOID, err := c.getMultiRangeElementOID(ctx, oid)
		if err != nil {
			return nil, err
		}

		dt, ok := c.TypeMap().TypeForOID(elementOID)
		if !ok {
			return nil, errors.New("multirange element OID not registered")
		}

		return &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.MultirangeCodec{ElementType: dt}}, nil
	default:
		return &pgtype.Type{}, errors.New("unknown typtype")
	}
}

func (c *Conn) getArrayElementOID(ctx context.Context, oid uint32) (uint32, error) {
	var typelem uint32

	err := c.QueryRow(ctx, "select typelem from pg_type where oid=$1", oid).Scan(&typelem)
	if err != nil {
		return 0, err
	}

	return typelem, nil
}

View on GitHub (pinned to ec1a0befd2)

Solutions

  1. LoadType and register the element type first, then the range, then the multirange.
  2. Use pg_range to find rngsubtype and ensure that OID is registered before calling LoadType on the multirange.
  3. Register a custom element codec via RegisterType if needed.
  4. Drive LoadType from a topologically-sorted dependency list.

Example fix

// before
mrt, err := conn.LoadType(ctx, "mymultirange") // element 'mytype' unknown
// -> "multirange element OID not registered"

// after
elem, err := conn.LoadType(ctx, "mytype")
if err != nil { return err }
conn.TypeMap().RegisterType(elem)
mrt, err := conn.LoadType(ctx, "mymultirange")
Defensive patterns

Strategy: validation

Validate before calling

// multirange element == underlying range element; ensure registered
var elemOID uint32
conn.QueryRow(ctx, "select rngsubtype from pg_range r join pg_type t on t.oid=r.rngtypid where t.typname=$1", multirangeName).Scan(&elemOID)
if _, ok := conn.TypeMap().TypeForOID(elemOID); !ok {
    // LoadType element, register, then LoadType multirange
}

Type guard

func multirangeElementRegistered(m *pgtype.Map, elemOID uint32) bool { _, ok := m.TypeForOID(elemOID); return ok }

Try / catch

if t, err := conn.LoadType(ctx, mrName); err != nil {
    if strings.Contains(err.Error(), "multirange element OID not registered") {
        // load+register element (and range), retry
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling Conn.LoadType on a custom multirange (discontiguous of a custom range) before the range's element type has been loaded and registered.

Common situations: Custom multiranges over custom ranges over custom subtypes; loading multirange before range before element; schema migrations that introduce multiranges last but register them first in code.

Related errors


AI-assisted analysis of jackc/pgx@ec1a0befd2 (2026-08-04). Data as JSON: /data/errors/0a4e0f4da8bf16c3.json. Report an issue: GitHub.