{"record":{"id":"24c67eb18303d389","repo":"jax-ml/jax","slug":"get-topology-for-devices-requires-1-devices","errorCode":null,"errorMessage":"get_topology_for_devices requires >= 1 devices.","messagePattern":"get_topology_for_devices requires >= 1 devices\\.","errorType":"validation","errorClass":"nb::value_error","httpStatus":null,"severity":"error","filePath":"jaxlib/jax.cc","lineNumber":621,"sourceCode":"           const absl::flat_hash_map<std::string, xla::PjRtValueType>& options)\n            -> std::shared_ptr<xla::ifrt::Topology> {\n          if (c_api.name() == nullptr ||\n              std::string_view(c_api.name()) != \"pjrt_c_api\") {\n            throw nb::value_error(\n                \"Argument to get_c_api_topology was not a pjrt_c_api capsule.\");\n          }\n          if (c_api.data() == nullptr) {\n            throw nb::value_error(\n                \"Argument to get_c_api_topology contained a null pointer.\");\n          }\n          return std::make_shared<xla::ifrt::PjRtTopology>(xla::ValueOrThrow(\n              xla::GetCApiTopology(static_cast<const PJRT_Api*>(c_api.data()),\n                                   topology_name, options)));\n        });\n  m.def(\"get_topology_for_devices\",\n        [](const std::vector<nb_class_ptr<PyDevice>>& py_devices) {\n          if (py_devices.empty()) {\n            throw nb::value_error(\n                \"get_topology_for_devices requires >= 1 devices.\");\n          }\n          auto client = py_devices[0]->client();\n          absl::InlinedVector<xla::ifrt::Device*, 1> ifrt_devices;\n          ifrt_devices.reserve(py_devices.size());\n          for (const auto& py_device : py_devices) {\n            if (py_device->client().get() != client.get()) {\n              throw nb::value_error(\n                  \"devices passed to get_topology_for_devices come from \"\n                  \"different clients.\");\n            }\n            ifrt_devices.push_back(py_device->device());\n          }\n          xla::ifrt::DeviceListRef device_list = xla::ValueOrThrow(\n              client->ifrt_client()->MakeDeviceList(ifrt_devices));\n          return xla::ValueOrThrow(\n              client->ifrt_client()->GetTopologyForDevices(device_list));\n        });","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jaxlib/jax.cc#L603-L639","documentation":"Constructing a topology from a device list is only meaningful with at least one device; an empty list gives no client or platform to derive the topology from, so the binding validates input and throws.","triggerScenarios":"jaxlib.get_topology_for_devices([]) — typically because the devices list was built from an empty slice, wrong device filter, or a filtering bug.","commonSituations":"Multi-process code slicing jax.devices() per process and accidentally producing an empty list (e.g. wrong process index).","solutions":["Check the list is non-empty before calling; debug why it's empty","Verify process/device slicing logic in multi-process jax jobs"],"exampleFix":"# before\njaxlib.get_topology_for_devices(my_devices)\n# after\nassert my_devices, 'no devices assigned to this process'\njaxlib.get_topology_for_devices(my_devices)","handlingStrategy":"validation","validationCode":"assert devices, 'need >= 1 device'","typeGuard":"def has_devices(devs) -> bool:\n    return len(devs) > 0","tryCatchPattern":null,"preventionTips":["Validate per-process device slices in multi-host jobs"],"tags":["jax","topology","devices","validation"],"backgroundTag":"empty-argument-list","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}