From 0264754b4381c0e0d128b5135cbd7a72cb924fb0 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Sun, 11 Oct 2026 17:14:01 +0100 Subject: [PATCH] Skip missing MuJoCo struct fields during named indexing --- dm_control/mujoco/index.py | 6 +++--- dm_control/mujoco/index_test.py | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dm_control/mujoco/index.py b/dm_control/mujoco/index.py index 76e534cc..f7dd7052 100644 --- a/dm_control/mujoco/index.py +++ b/dm_control/mujoco/index.py @@ -636,9 +636,9 @@ def struct_indexer(struct, struct_name, size_to_axis_indexer): for field_name in array_sizes: - # Skip over structured arrays and fields that have sizes but aren't numpy - # arrays, such as text fields and contacts (b/34805932). - attr = getattr(struct, field_name) + # Skip fields removed by newer MuJoCo versions even if older generated + # metadata still lists them, as well as non-array and structured fields. + attr = getattr(struct, field_name, None) if not isinstance(attr, np.ndarray) or attr.dtype.fields: continue diff --git a/dm_control/mujoco/index_test.py b/dm_control/mujoco/index_test.py index 3eaeae53..47f5d220 100644 --- a/dm_control/mujoco/index_test.py +++ b/dm_control/mujoco/index_test.py @@ -14,6 +14,7 @@ # ============================================================================ import collections +from unittest import mock from absl.testing import absltest from absl.testing import parameterized @@ -73,6 +74,25 @@ def setUp(self): self._data_indexers = index.struct_indexer(self._data, 'mjdata', self._size_to_axis_indexer) + def testStructIndexerSkipsRemovedDataFields(self): + # MuJoCo may remove a field that remains in generated array_sizes. + with mock.patch.dict(sizes.array_sizes['mjdata'], + {'removed_data_field': ('nbody',)}): + named = index.struct_indexer( + self._data, 'mjdata', self._size_to_axis_indexer) + self.assertNotIn('removed_data_field', named._asdict()) + self.assertIn('xpos', named._asdict()) + np.testing.assert_array_equal( + named.xpos['cart'], self._data.xpos[1]) + + def testStructIndexerSkipsRemovedModelFields(self): + with mock.patch.dict(sizes.array_sizes['mjmodel'], + {'removed_model_field': ('ngeom',)}): + named = index.struct_indexer( + self._model, 'mjmodel', self._size_to_axis_indexer) + self.assertNotIn('removed_model_field', named._asdict()) + self.assertIn('geom_rgba', named._asdict()) + def assertIndexExpressionEqual(self, expected, actual): try: if isinstance(expected, tuple):