---
title: Migrating from 0.31 to 0.32
order: 978
---

## "Data loaders" renamed to "importers"

The file import system previously called "data loaders" has been renamed to "importers" to avoid confusion with the widely-used ML/PyTorch "dataloader" concept and to better describe what the system does: importing external file formats into Rerun.

The old names are deprecated but most of them still work for this release.

### Rust API

| Before | After |
|--------|-------|
| `rerun::DataLoader` | `rerun::Importer` |
| `rerun::DataLoaderSettings` | `rerun::ImporterSettings` |
| `rerun::DataLoaderError` | `rerun::ImporterError` |
| `rerun::LoadedData` | `rerun::ImportedData` |
| `rerun::EXTERNAL_DATA_LOADER_PREFIX` | `rerun::EXTERNAL_IMPORTER_PREFIX` |
| `rerun::EXTERNAL_DATA_LOADER_INCOMPATIBLE_EXIT_CODE` | `rerun::EXTERNAL_IMPORTER_INCOMPATIBLE_EXIT_CODE` |

All old names are available as deprecated type aliases and will be removed in a future release.

The Cargo feature flag `data_loaders` has been renamed to `importers`.

### C/C++ API

| Before | After |
|--------|-------|
| `rr_data_loader_settings` | `rr_importer_settings` |
| `EXTERNAL_DATA_LOADER_INCOMPATIBLE_EXIT_CODE` | `EXTERNAL_IMPORTER_INCOMPATIBLE_EXIT_CODE` |

The old names are still available as deprecated aliases.

### Python API

| Before | After |
|--------|-------|
| `rr.EXTERNAL_DATA_LOADER_INCOMPATIBLE_EXIT_CODE` | `rr.EXTERNAL_IMPORTER_INCOMPATIBLE_EXIT_CODE` |

The old name is still available but deprecated.

### External importers

Executables on `$PATH` with the `rerun-importer-` prefix are now the canonical way to register external importers. The old `rerun-loader-` prefix continues to work but will log a deprecation warning.

Before:

```
rerun-loader-my-format
```

After:

```
rerun-importer-my-format
```

## Lenses API (Rust)

The Lenses API has been restructured and simplified.

### Entity path filtering moved to `Lenses`

Entity path filtering is no longer part of the `Lens` itself. Use
`Lenses::add_lens_with_filter`:

```rust
let lenses = Lenses::new(OutputMode::DropUnmatched)
    .add_lens_with_filter(EntityPathFilter::parse_forgiving("sensors/**"), lens);
```

This makes applying lenses to individual chunks more ergonomic.

### New builder API

Lenses are now created through `Lens::derive()`, `Lens::scatter()`, and `Lens::mutate()`:

```rust
// Before
Lens::for_input_column(EntityPathFilter::all(), "component")
    .output_columns(|out| { /* … */ })?
    .build()

// After - derive lens (1:1 row mapping)
Lens::derive("component")
    .to_component(component_descr, ".field")
    .build()?

// After - scatter lens (1:N row mapping)
Lens::scatter("component")
    .output_entity("/target")
    .to_component(component_descr, ".field")
    .build()?

// After - mutate lens (modifies component in-place)
Lens::mutate("component", ".field").build()
```

To output columns to multiple entities from a single component, multiple lenses can be registered for the same input component.

## `rerun rrd compact` renamed to `rerun rrd optimize`, has profiles and new defaults

`rerun rrd compact` is now `rerun rrd optimize`.

A new `--profile` argument has been added to opt to known good values.
Two profiles are available: `live` (optimized for the live Viewer workflow, same as previous defaults) and `object-store` (optimized for querying and streaming from object-store-backed storage, e.g. a catalog server). <!-- NOLINT -->

By default, the `object-store` profile is now used. Use `--profile live` to keep the previous behavior. <!-- NOLINT -->

## `DatasetEntry.register` requires a sequence of URIs (Python)

`DatasetEntry.register` no longer accepts a single URI string for `recording_uri`.
Pass a sequence of URIs instead, and prefer batching many URIs into a single `register` call rather than calling `register` repeatedly in a loop (which is much slower).

Old single-string invocations still work at runtime but emit a `DeprecationWarning`.

```diff
- dataset.register(url, layer_name="base")
+ dataset.register([url], layer_name="base")
```

`layer_name` is unchanged: pass a single string to apply one layer to all recordings, or a sequence matching the length of `recording_uri`.

## URDF importer transform entity

The [URDF importer](../../howto/logging-and-ingestion/urdf.md) now loads the static transforms of the model to the `/tf_static` entity by default.
This replaces the model-dependent entity path of previous versions, and improves consistency with ROS data.

A custom entity path can be now also configured in the `UrdfTree` API in Python and Rust, if desired.

## MCAP metadata and statistics

In MCAP to RRD conversion, metadata records, statistics, and recording info are now saved at dedicated [reserved entity paths](../../concepts/logging-and-ingestion/entity-path.md#reserved-paths) instead of RRD properties (`__properties`).

Metadata records are saved under `__mcap_metadata`, and MCAP statistics and recording info are saved under `__mcap_properties`.

## `rerun.recording` deprecated in favor of `RrdReader`

The `rerun.recording` module — `Recording`, `RRDArchive`, `load_recording`, `load_archive` — is deprecated.
Use `rerun.experimental.RrdReader` instead, which natively supports multi-store RRDs (multiple recordings and blueprints in one file) and lazy loading.

| Before                                               | After                                                                                     |
|------------------------------------------------------|-------------------------------------------------------------------------------------------|
| `rr.recording.load_recording(path)`                  | `rr.experimental.RrdReader(path).store()`                                                 |
| `rr.recording.load_archive(path)`                    | `rr.experimental.RrdReader(path)`                                                         |
| `archive.all_recordings()`                           | `reader.recordings()` then `reader.store(store=entry)`                                    |
| `recording.application_id()` / `recording_id()`      | `StoreEntry.application_id` / `recording_id` (from `reader.recordings()`)                 |
| `Recording.from_chunks(chunks, app, rec).save(path)` | `LazyChunkStream.from_iter(chunks).write_rrd(path, application_id=app, recording_id=rec)` |
| `rr.send_recording(rec)`                             | `rr.experimental.send_chunks(reader.store())`                                             |
| `RecordingStream.send_recording(rec)`                | `RecordingStream.send_chunks(rec)`                                                        |
| `DatasetEntry.download_segment(seg)`                 | `DatasetEntry.segment_store(seg)`                                                         |
