Collection of minor issues found by an automated bug hunt (validation edge cases, misleading errors, small inefficiencies)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 394
- Forks
- 95
- Avg merge
- 4d 3h
- Merged PRs (30d)
- 7
Description
[!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 thespatialdatacode base. It has not been verified or triaged by a human yet; theneeds: triagelabel 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
Each item was confirmed with a script; the repro below demonstrates items 1–8.
- Empty
GeoDataFrame→IndexErrorinShapesModel.parseandget_axes_names(.iloc[0]) instead of theValueError("Column geometry is empty")thatvalidatewould raise. TableModel.parse(region=("a", "b"))(tuple) fails with the misleadingadata.obs[region] values do not match with region values;region=pd.Index([...])fails withTypeError: unhashable type: 'Index'. Onlylist/np.ndarrayare normalised.RasterSchema.parse(chunks=<float>)crashes withValueError: not enough values to unpack({dim: chunks for index, dim in data.dims}iterates dimension names).RasterSchema.parse(DataArray)mutates its input: a numpy-backedDataArrayhas its.datareplaced by a dask array in place.PointsModel.validaterejects pandas nullable / pyarrow numeric coordinate dtypes (Int64,float64[pyarrow]) while accepting every numpy int/float — usepd.api.types.is_numeric_dtype.get_element_instances(shapes_or_points, return_background=True)→TypeError: _() got an unexpected keyword argument(the GeoDataFrame/DaskDataFrame overloads lack the keyword the generic signature advertises).set_table_annotates_spatialelement(region=pd.Series([...]))— the type hint allows a Series, but the Series object is stored inuns["spatialdata_attrs"]["region"];TableModel.validatethen fails withunhashable type: 'Series'and the table cannot be written.filter_by_table_query(table_name, element_names=[...])raisesKeyError: 't'when no table row annotates the requested elements (subset(filter_tables=True)drops the empty table beforesdata_subset.tables[table_name]).
Not in the script:
- CLI:
python -m spatialdata peek <store> tablesilently drops the tables — the clickChoiceoffers"table"(singular) butread_zarrexpects"tables", andtablesis rejected by click. TableModel.parseuniqueness check computesgroupby(region).nunique()over all obs columns instead of only the instance key (10–100× slower; 0.35 s vs 0.02 s for 1 M rows × 54 columns). Usegrouped[instance_key].nunique().relabel_sequentialbreaks when the internal lookup array spans several dask chunks (max_label> ~16 M with the default chunk size):ValueError: Dimension 1 has N blocks, adjust_chunks specified with 1 blocks.rechunk(-1)the lookup array.polygon_query(circles, clip=True)returns Polygon geometries but keeps theradiuscolumn.to_polygons(circles)buffers row-by-row withDataFrame.apply(axis=1)(2.1 s for 167 k Xenium circles) whereasshapely.buffer(geoms, radii, quad_segs=...)takes 0.58 s with identical areas.map_rasterdocstring: changing dimensionality needsdrop_axis/new_axis(forwarded via**kwargs) in addition todims; with onlydims=("y", "x")the call fails with number of dimensions of the output data (3) differs....get_transformation_between_landmarksdocstring example usesPointsModel(points_moving)instead ofPointsModel.parse(...);_get_current_output_axescontains a dead statementset(transformation.map_axis.keys()).- Compressor level docs inconsistent:
_validate_compressor_argsmessages say 'between 1 and 9' in two places and 'between 0 and 9' in another; level 0 is accepted. - Docstring/signature mismatches:
rasterize_binsdocumentsreturn_regions_as_labels(parameter:return_region_as_labels);SpatialData.subsetdocumentsfilter_table(parameter:filter_tables);validate_axesdocumentsaxis(parameter:axes);get_extent's generic docstring lists its parameters under 'Returns';get_transformation_between_coordinate_systemsdoes not documentsdata/intermediate_coordinate_systems; the points↔geopandas converters do not documentsuppress_z_warning. - Forward compatibility:
Pandas4Warning: The copy keyword is deprecatedfromtable.obs.rename(columns=..., copy=False)inconcatenate._concatenate_tables(will raise in pandas 4). ShapesModel.validateinspects only the first geometry, so[Polygon, Point]without a radius column is accepted;validate_shapes_not_mixed_typesexists but is opt-in.- Table filtering via left join computes
da.uniqueon full-resolution labels insubset(),filter_by_coordinate_system()and spatial queries (0.1 s per 3648×5472 labels, 1.8 s forfilter_by_coordinate_systemon the 30-labels CosMx dataset); forfilter_by_coordinate_systema name-based filter would be O(n_rows). (See also #1177.)
Severity (agent's assessment): low (each item)
Where: various, see list
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",
# ]
# ///
"""Collection of small issues, each demonstrated below (see the issue text for the full list)."""
import warnings
import numpy as np
import pandas as pd
import geopandas as gpd
from anndata import AnnData
from shapely.geometry import Point
from spatialdata import SpatialData, get_element_instances
from spatialdata.models import Image2DModel, PointsModel, ShapesModel, TableModel
warnings.simplefilter("ignore")
found = []
print("1. empty GeoDataFrame -> IndexError instead of a clear ValueError")
try:
ShapesModel.parse(gpd.GeoDataFrame({"geometry": gpd.GeoSeries([], dtype="geometry")}))
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:60]); found.append(type(e).__name__ == "IndexError")
print("2. TableModel.parse(region=tuple / pd.Index) -> misleading errors")
obs = pd.DataFrame({"region": pd.Categorical(["a", "a", "b", "b"]), "instance_id": [0, 1, 0, 1]})
for reg in [("a", "b"), pd.Index(["a", "b"])]:
try:
TableModel.parse(AnnData(X=np.zeros((4, 1)), obs=obs.copy()), region=reg, region_key="region", instance_key="instance_id")
print(" ", type(reg).__name__, "OK")
except Exception as e: # noqa: BLE001
print(" ", type(reg).__name__, "->", type(e).__name__, str(e)[:70]); found.append(True)
print("3. RasterSchema.parse(chunks=<float>) -> ValueError from a wrong comprehension")
try:
Image2DModel.parse(np.zeros((1, 8, 8)), scale_factors=[2], chunks=4.0)
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:70]); found.append(True)
print("4. RasterSchema.parse mutates a numpy-backed input DataArray in place")
import xarray as xr
arr = xr.DataArray(np.zeros((1, 4, 4)), dims=("c", "y", "x"))
Image2DModel.parse(arr)
print(" input .data type after parse:", type(arr.data).__name__); found.append(type(arr.data).__name__ != "ndarray")
print("5. PointsModel.validate rejects pandas nullable / pyarrow numeric coordinate dtypes")
for dt in ["Int64", "float64[pyarrow]"]:
try:
PointsModel.parse(pd.DataFrame({"x": pd.array([1, 2], dtype=dt), "y": [1.0, 2.0]})); print(" ", dt, "OK")
except Exception as e: # noqa: BLE001
print(" ", dt, "->", type(e).__name__, str(e)[:60]); found.append(True)
print("6. get_element_instances(shapes, return_background=True) -> TypeError (overload lacks the keyword)")
shp = ShapesModel.parse(gpd.GeoDataFrame({"geometry": [Point(0, 0)], "radius": [1.0]}))
try:
get_element_instances(shp, return_background=True)
except Exception as e: # noqa: BLE001
print(" ", type(e).__name__, str(e)[:70]); found.append(True)
print("7. set_table_annotates_spatialelement(region=pd.Series) stores the Series in uns -> validate fails")
obs2 = pd.DataFrame({"region": pd.Categorical(["shp"]), "instance_id": [0]})
t = TableModel.parse(AnnData(X=np.zeros((1, 1)), obs=obs2), region="shp", region_key="region", instance_key="instance_id")
sdata = SpatialData(shapes={"shp": shp}, tables={"t": t})
sdata.set_table_annotates_spatialelement("t", region=pd.Series(["shp"]))
try:
TableModel.validate(sdata["t"]); print(" validate OK")
except Exception as e: # noqa: BLE001
print(" validate ->", type(e).__name__, str(e)[:60]); found.append(True)
print("8. filter_by_table_query(element_names=[...]) -> KeyError when no table row annotates those elements")
other = ShapesModel.parse(gpd.GeoDataFrame({"geometry": [Point(5, 5)], "radius": [1.0]}))
t2 = TableModel.parse(AnnData(X=np.zeros((1, 1)), obs=obs2.copy()), region="shp", region_key="region", instance_key="instance_id")
sdata2 = SpatialData(shapes={"shp": shp, "other": other}, tables={"t": t2})
try:
sdata2.filter_by_table_query("t", element_names=["other"]); print(" OK")
except Exception as e: # noqa: BLE001
print(" ->", type(e).__name__, str(e)[:60]); found.append(True)
print("VERDICT:", "BUG REPRODUCED" if all(found) and found else "NOT REPRODUCED")
Observed output
1. empty GeoDataFrame -> IndexError instead of a clear ValueError
IndexError single positional indexer is out-of-bounds
2. TableModel.parse(region=tuple / pd.Index) -> misleading errors
tuple -> ValueError `adata.obs[region]` values do not match with `region` values.
Index -> TypeError unhashable type: 'Index'
3. RasterSchema.parse(chunks=<float>) -> ValueError from a wrong comprehension
ValueError not enough values to unpack (expected 2, got 1)
4. RasterSchema.parse mutates a numpy-backed input DataArray in place
input .data type after parse: Array
5. PointsModel.validate rejects pandas nullable / pyarrow numeric coordinate dtypes
Int64 -> ValueError Column `x` must be of type `int` or `float`.
float64[pyarrow] -> ValueError Column `x` must be of type `int` or `float`.
6. get_element_instances(shapes, return_background=True) -> TypeError (overload lacks the keyword)
TypeError _() got an unexpected keyword argument 'return_background'
7. set_table_annotates_spatialelement(region=pd.Series) stores the Series in uns -> validate fails
validate -> TypeError unhashable type: 'Series'
8. filter_by_table_query(element_names=[...]) -> KeyError when no table row annotates those elements
-> KeyError 't'
VERDICT: BUG REPRODUCED
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). Also reproduced in a second environment with pandas 2.3.3 / anndata 0.12.11 / numpy 2.4.4 / zarr 3.2.1.
Possibly related issues
#1177
Automatically generated; discovered by an AI agent (Claude) and not yet reviewed by a human.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running repro.py with the pinned uv environment and compare each reproduced item with the current main branch. The report spans ShapesModel, TableModel, RasterSchema, PointsModel, filtering, CLI, documentation, and performance paths, so first separate and triage the confirmed findings. Done means each accepted issue has a clear scope, relevant tests or documentation updates, and a maintainer-approved follow-up.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- pandas, python
- Domain
- backend, cli, data, documentation, performance, testing-qa
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 20/100