Skip to content

aggregate(values, by=<multiscale labels>) crashes with AttributeError despite DataTree being a declared input type #1249

Open

Description

@LucaMarconato

Note

This whole message is AI-generated. The issue was automatically discovered and reported by an AI agent (Claude) during an autonomous bug hunt on the spatialdata code base. It has not been verified or triaged by a human yet; the needs: triage label is set so that a maintainer can confirm it. The reproduction script below was executed by the agent in an isolated environment (see Environment) and its output is pasted verbatim.

Summary

Found while triaging #215/#216 (an existing tracking issue asking to add tests for aggregation with multiscale objects, and its follow-up listing unsupported values/by combinations). aggregate(values=<image>, by=<multiscale labels>) fails with AttributeError: 'DataTree' object has no attribute 'dtype', even though both the function signature and the label-aggregation code path treat DataTree as a supported by type for labels — the crash happens one level up, in the table-construction helper that was not updated for the DataTree case. Multiscale images in values (aggregated by single- or multiscale labels) work correctly and return the same result as the single-scale image.

Severity (agent's assessment): medium — crash on a documented/typed use case, no workaround other than manually collapsing to single-scale labels first

Where: src/spatialdata/_core/operations/aggregate.py::_create_sdata_from_table_and_shapes, line shapes_index_dtype = shapes.index.dtype if isinstance(shapes, GeoDataFrame) else shapes.dtype (~line 227); the type hint of the shapes parameter and of aggregate()'s by parameter both include DataTree, and _aggregate_image_by_labels already handles a DataTree by (it calls get_pyramid_levels(by, n=0) to get the finest scale before computing), but the result is then passed into _create_sdata_from_table_and_shapes still as the original DataTree.

Expected behaviour

aggregate(image, by=<multiscale labels>) returns the same table as aggregate(image, by=<the finest single-scale level of those labels>), instead of raising.

Reproduction

Save as repro.py and run uv run repro.py (the PEP 723 header pins spatialdata to the commit the bug was found on; replace the URL fragment with @main to test the current main branch).

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "spatialdata @ git+https://github.com/scverse/spatialdata.git@ccf1ea048d054b6624214bf618008a9f9ae223e0",
# ]
# ///
"""New bug found while triaging #215/#216: `aggregate()` declares `by: DataTree` (multiscale labels) as
supported and `_aggregate_image_by_labels` handles it, but `_create_sdata_from_table_and_shapes` then
calls `shapes.dtype` on the DataTree -> AttributeError."""
import numpy as np

from spatialdata import aggregate
from spatialdata.models import Image2DModel, Labels2DModel

rng = np.random.default_rng(0)
img = Image2DModel.parse(rng.random((2, 32, 32)))
lab = np.zeros((32, 32), dtype=np.int32); lab[2:12, 2:12] = 5; lab[16:30, 16:30] = 9
labels_ms = Labels2DModel.parse(lab, scale_factors=[2, 2])
try:
    t = aggregate(values=img, by=labels_ms, agg_func="mean")["table"]
    print("aggregate(image, multiscale labels) -> OK", t.shape)
    print("VERDICT: NOT REPRODUCED")
except Exception as e:  # noqa: BLE001
    print("aggregate(image, multiscale labels) ->", type(e).__name__, str(e)[:120])
    print("VERDICT: REPRODUCED (multiscale labels in `by` crash with AttributeError although the signature declares DataTree support)")

Observed output

aggregate(image, multiscale labels) -> AttributeError 'DataTree' object has no attribute 'dtype'
VERDICT: REPRODUCED (multiscale labels in `by` crash with AttributeError although the signature declares DataTree support)

Possible fix direction (unverified)

In _create_sdata_from_table_and_shapes, resolve shapes to its finest single-scale level (e.g. via get_pyramid_levels(shapes, n=0), mirroring what _aggregate_image_by_labels already does) before reading .dtype/.index, or thread the resolved single-scale array through from the caller instead of the original DataTree.

Environment

uv run repro.py with the PEP 723 metadata in the script (fresh, isolated environment; spatialdata built from main @ ccf1ea0 (2026-08-28); Python 3.13, latest releases of the dependencies at run time: pandas 3.0, anndata 0.13, zarr 3.3, dask 2026.8, numpy 2.5, geopandas 1.1, shapely 2.1). macOS (arm64).

Possibly related issues

#215, #216


Automatically generated; discovered by an AI agent (Claude) and not yet reviewed by a human.

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