Skip to content

[R] ExtensionType$new() crashes on non-R extension types such as arrow.opaque #53951

Open

Description

@pedrobtz

Describe the bug, including details regarding any error messages, version, and platform.

Making an R object for an extension type that was not defined in R (a C++ extension type such as the canonical arrow.opaque) crashes R. Everything that goes through ExtensionType$new() does it: as_data_type() on such a schema, as_arrow_array(), arrow_table(), and so on.

Minimal reproduction, using only nanoarrow to build the schema:

library(nanoarrow)
s <- na_fixed_size_binary(16)
s <- nanoarrow_schema_modify(s, list(metadata = list(
  "ARROW:extension:name" = "arrow.opaque",
  "ARROW:extension:metadata" = '{"type_name":"hugeint","vendor_name":"DuckDB"}'
)))
arrow::as_data_type(s)
 *** caught segfault ***          (arrow 25.0.1; a bus error with arrow 24.0.0)
Traceback:
 1: ExtensionType__r6_class(self)
 2: super$r6_class()
 3: ExtensionType$new(<pointer: 0x...>)
 4: getFromNamespace("DataType", "arrow")$import_from_c(exportable_schema)
 5: as_data_type.nanoarrow_schema(s)

Seen with arrow 25.0.1 and 24.0.0 (CRAN binaries), nanoarrow 0.9.0, R 4.6.1 on macOS arm64; also as a segfault through arrow_table(). Tables built without making that R object (for example as_arrow_table() on a nanoarrow struct array, or reading Parquet) carry the column fine.

Cause. ExtensionType__r6_class() in r/src/extension-impl.cpp casts any arrow::ExtensionType to RExtensionType:

cpp11::environment ExtensionType__r6_class(
    const std::shared_ptr<arrow::ExtensionType>& type) {
  auto r_type =
      arrow::internal::checked_pointer_cast<RExtensionType, arrow::ExtensionType>(type);
  return r_type->r6_class();
}

checked_pointer_cast is a static_pointer_cast in release builds, so for an extension type that is not an RExtensionType, such as arrow::extension::OpaqueType, r6_class() reads unrelated memory. This is the same bug as #49956 in the GLib bindings (an unconditional cast to GExtensionType), which names R-arrow among the affected bindings but was fixed in GLib only.

A fix in the same spirit: dynamic_pointer_cast to RExtensionType, and when that fails return the plain ExtensionType R6 class (or a dedicated class for canonical extension types) instead of dereferencing.

Found while bridging DuckDB's HUGEINT/UHUGEINT (exported as arrow.opaque with arrow_lossless_conversion = true) into R.


Co-authored with Claude Code (Anthropic).

Component(s)

R

Activity

  1. added a commit that references this issue on Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions