jax-ml/jax · error · nb::value_error

Argument to get_c_api_topology contained a null pointer.

Error message

Argument to get_c_api_topology contained a null pointer.

What it means

After the pjrt_c_api capsule is validated, its pointer is dereferenced to reach the topology extension. It fires when the capsule's stored pointer is NULL, so no API table can be read.

Solutions

  1. Ensure the plugin loaded successfully before distributing its capsule
  2. Re-obtain the capsule from load_pjrt_plugin and check it is non-null

Example fix

# before
assert cap is not None
jaxlib.get_c_api_topology(cap, 'topo')
# after
cap = jaxlib.load_pjrt_plugin(p, library_path)
assert cap is not None and ctypes.cast(cap, ctypes.c_void_p).value
jaxlib.get_c_api_topology(cap, 'topo')
Defensive patterns

Strategy: validation

Validate before calling

import ctypes
assert ctypes.cast(c_api, ctypes.c_void_p).value is not None

Prevention

When it happens

Trigger: Passing a capsule constructed as nb::capsule(nullptr, 'pjrt_c_api') or a capsule whose pointed-to API was already freed.

Common situations: Plugins that return empty capsules on failed initialization; use-after-free of a plugin API handle.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/9e910a8cf47e7f52. Report an issue: GitHub.

Appendix: source

Thrown at jaxlib/jax.cc:611

  // standard registration.
  m.def("get_default_c_api_topology",
        [](std::string platform_name, std::string topology_name,
           const absl::flat_hash_map<std::string, xla::PjRtValueType>& options)
            -> std::shared_ptr<xla::ifrt::Topology> {
          return std::make_shared<xla::ifrt::PjRtTopology>(xla::ValueOrThrow(
              xla::GetCApiTopology(platform_name, topology_name, options)));
        });
  m.def("get_c_api_topology",
        [](nb::capsule c_api, std::string topology_name,
           const absl::flat_hash_map<std::string, xla::PjRtValueType>& options)
            -> std::shared_ptr<xla::ifrt::Topology> {
          if (c_api.name() == nullptr ||
              std::string_view(c_api.name()) != "pjrt_c_api") {
            throw nb::value_error(
                "Argument to get_c_api_topology was not a pjrt_c_api capsule.");
          }
          if (c_api.data() == nullptr) {
            throw nb::value_error(
                "Argument to get_c_api_topology contained a null pointer.");
          }
          return std::make_shared<xla::ifrt::PjRtTopology>(xla::ValueOrThrow(
              xla::GetCApiTopology(static_cast<const PJRT_Api*>(c_api.data()),
                                   topology_name, options)));
        });
  m.def("get_topology_for_devices",
        [](const std::vector<nb_class_ptr<PyDevice>>& py_devices) {
          if (py_devices.empty()) {
            throw nb::value_error(
                "get_topology_for_devices requires >= 1 devices.");
          }
          auto client = py_devices[0]->client();
          absl::InlinedVector<xla::ifrt::Device*, 1> ifrt_devices;
          ifrt_devices.reserve(py_devices.size());
          for (const auto& py_device : py_devices) {
            if (py_device->client().get() != client.get()) {
              throw nb::value_error(

View on GitHub (pinned to 1e1c6a8fc0)