{"id":"0a4e0f4da8bf16c3","repo":"jackc/pgx","slug":"multirange-element-oid-not-registered","errorCode":null,"errorMessage":"multirange element OID not registered","messagePattern":"multirange element OID not registered","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"conn.go","lineNumber":1359,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := c.TypeMap().TypeForOID(elementOID)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"range element OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.RangeCodec{ElementType: dt}}, nil\n\tcase \"m\": // multirange\n\t\telementOID, err := c.getMultiRangeElementOID(ctx, oid)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tdt, ok := c.TypeMap().TypeForOID(elementOID)\n\t\tif !ok {\n\t\t\treturn nil, errors.New(\"multirange element OID not registered\")\n\t\t}\n\n\t\treturn &pgtype.Type{Name: typeName, OID: oid, Codec: &pgtype.MultirangeCodec{ElementType: dt}}, nil\n\tdefault:\n\t\treturn &pgtype.Type{}, errors.New(\"unknown typtype\")\n\t}\n}\n\nfunc (c *Conn) getArrayElementOID(ctx context.Context, oid uint32) (uint32, error) {\n\tvar typelem uint32\n\n\terr := c.QueryRow(ctx, \"select typelem from pg_type where oid=$1\", oid).Scan(&typelem)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn typelem, nil\n}","sourceCodeStart":1341,"sourceCodeEnd":1377,"githubUrl":"https://github.com/jackc/pgx/blob/ec1a0befd22592cffffdeeb0a50311b506372f4c/conn.go#L1341-L1377","documentation":"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.","triggerScenarios":"Calling Conn.LoadType on a custom multirange (discontiguous of a custom range) before the range's element type has been loaded and registered.","commonSituations":"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.","solutions":["LoadType and register the element type first, then the range, then the multirange.","Use pg_range to find rngsubtype and ensure that OID is registered before calling LoadType on the multirange.","Register a custom element codec via RegisterType if needed.","Drive LoadType from a topologically-sorted dependency list."],"exampleFix":"// before\nmrt, err := conn.LoadType(ctx, \"mymultirange\") // element 'mytype' unknown\n// -> \"multirange element OID not registered\"\n\n// after\nelem, err := conn.LoadType(ctx, \"mytype\")\nif err != nil { return err }\nconn.TypeMap().RegisterType(elem)\nmrt, err := conn.LoadType(ctx, \"mymultirange\")","handlingStrategy":"validation","validationCode":"// multirange element == underlying range element; ensure registered\nvar elemOID uint32\nconn.QueryRow(ctx, \"select rngsubtype from pg_range r join pg_type t on t.oid=r.rngtypid where t.typname=$1\", multirangeName).Scan(&elemOID)\nif _, ok := conn.TypeMap().TypeForOID(elemOID); !ok {\n    // LoadType element, register, then LoadType multirange\n}","typeGuard":"func multirangeElementRegistered(m *pgtype.Map, elemOID uint32) bool { _, ok := m.TypeForOID(elemOID); return ok }","tryCatchPattern":"if t, err := conn.LoadType(ctx, mrName); err != nil {\n    if strings.Contains(err.Error(), \"multirange element OID not registered\") {\n        // load+register element (and range), retry\n    }\n    return nil, err\n}","preventionTips":["Register element, then range, then multirange — in that order.","Resolve rngsubtype from pg_range before loading containers.","Maintain a dependency graph for custom types."],"tags":["types","loadtype","registration","multirange"],"analyzedSha":"ec1a0befd22592cffffdeeb0a50311b506372f4c","analyzedAt":"2026-08-04T22:52:11.263Z","schemaVersion":2}