From d18fe3593beb8e95b88053caf9bc9d7e2302c432 Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Mon, 11 May 2026 20:14:24 -0700 Subject: [PATCH 1/2] fix: raise on instance_id=0 in render_labels annotation table Closes #607. Signed-off-by: SAY-5 --- src/spatialdata_plot/pl/render.py | 7 +++++++ tests/pl/test_render_labels.py | 32 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/spatialdata_plot/pl/render.py b/src/spatialdata_plot/pl/render.py index d97bb474..7ddd957f 100644 --- a/src/spatialdata_plot/pl/render.py +++ b/src/spatialdata_plot/pl/render.py @@ -1654,6 +1654,13 @@ def _render_labels( _, region_key, instance_key = get_table_keys(sdata[table_name]) table = sdata[table_name][sdata[table_name].obs[region_key].isin([element])] + if (table.obs[instance_key] == 0).any(): + raise ValueError( + f"Table '{table_name}' contains instance_id=0 for element '{element}'. Label value 0 is " + "reserved for background and must not appear in the annotation table. Remove the row with " + "instance_id=0 before plotting." + ) + # get instance id based on subsetted table instance_id = np.unique(table.obs[instance_key].values) diff --git a/tests/pl/test_render_labels.py b/tests/pl/test_render_labels.py index e2684e24..106cdddd 100644 --- a/tests/pl/test_render_labels.py +++ b/tests/pl/test_render_labels.py @@ -490,3 +490,35 @@ def test_render_labels_rejects_float_dtype(dtype): sdata.pl.render_labels("lbl").pl.show(ax=ax) finally: plt.close(fig) + + +def test_render_labels_rejects_background_instance_id_in_table(): + # Regression test for #607: a table row with instance_id=0 (background label) + # used to crash with `IndexError: Boolean index has wrong length` from deep + # inside the color-vector mask, with no mention of background label 0. + labels_data = np.zeros((20, 20), dtype=np.int32) + labels_data[3:8, 3:8] = 1 + labels_data[12:17, 12:17] = 2 + labels = Labels2DModel.parse(labels_data, dims=["y", "x"]) + + obs = pd.DataFrame( + { + "region": pd.Categorical(["lbl"] * 3), + "instance_id": [0, 1, 2], + "score": [99.0, 1.0, 2.0], + } + ) + table = TableModel.parse( + AnnData(X=np.zeros((3, 1)), obs=obs), + region="lbl", + region_key="region", + instance_key="instance_id", + ) + sdata = SpatialData(labels={"lbl": labels}, tables={"t": table}) + + fig, ax = plt.subplots() + try: + with pytest.raises(ValueError, match=r"instance_id=0.*background"): + sdata.pl.render_labels("lbl", color="score", table_name="t").pl.show(ax=ax) + finally: + plt.close(fig) From f3eea16b27efd7cbe6e1922b07b1e7212d82be2b Mon Sep 17 00:00:00 2001 From: Tim Treis Date: Tue, 12 May 2026 10:44:41 +0200 Subject: [PATCH 2/2] Update test_render_labels.py --- tests/pl/test_render_labels.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/pl/test_render_labels.py b/tests/pl/test_render_labels.py index 106cdddd..431a0dc1 100644 --- a/tests/pl/test_render_labels.py +++ b/tests/pl/test_render_labels.py @@ -493,9 +493,8 @@ def test_render_labels_rejects_float_dtype(dtype): def test_render_labels_rejects_background_instance_id_in_table(): - # Regression test for #607: a table row with instance_id=0 (background label) - # used to crash with `IndexError: Boolean index has wrong length` from deep - # inside the color-vector mask, with no mention of background label 0. + # Regression test for #607: table row with instance_id=0 (background) + # used to crash with obnscure error. labels_data = np.zeros((20, 20), dtype=np.int32) labels_data[3:8, 3:8] = 1 labels_data[12:17, 12:17] = 2