Skip to content

spatial_scatter uses inconsistent spatial_key for image validation and loading #1269

Description

@hwr9912

Report

When using sq.pl.spatial_scatter() with a custom spatial_key, I noticed inconsistent behavior in how background images are validated and loaded.

My AnnData contains two coordinate systems:

adata.obsm["spatial"]
adata.obsm["spatial_trans"]

I want to use spatial_trans for plotting:

sq.pl.spatial_scatter(
    adata,
    spatial_key="spatial_trans",
    img="hires",
    img_res_key="hires",
    ...
)

The issue appears to be in src/squidpy/pl/_spatial_utils.py, around lines 166–173:

image_mapping = Key.uns.library_mapping(adata, spatial_key, Key.uns.image_key, library_id)

if img_res_key is None:
    img_res_key = _get_unique_map(image_mapping)[0]
elif img_res_key not in _get_unique_map(image_mapping):
    raise KeyError(
        f"Image key: `{img_res_key}` does not exist. Available image keys: `{image_mapping.values()}`"
    )

img = [adata.uns[Key.uns.spatial][i][Key.uns.image_key][img_res_key] for i in library_id]

Here, the image validation around line 166 uses:

adata.uns[spatial_key][library_id]["images"]

while the actual image loading around line 173 uses:

adata.uns["spatial"][library_id]["images"]

This leads to inconsistent behavior whenever spatial_key != "spatial":

  • If images are stored under adata.uns["spatial_trans"], validation succeeds, but image loading fails because the image is actually loaded from adata.uns["spatial"].
  • If images are stored under adata.uns["spatial"], image loading would be correct, but validation fails first because library_mapping() checks adata.uns["spatial_trans"].

For example, with images stored under adata.uns["spatial"], I get:

KeyError: "`images` not found in `adata.uns['spatial_trans']['library_id'])`
with following `library_id`: ['sham', 'MCAO_1d', 'MCAO_3d', 'MCAO_14d']."

I think either line 166 or line 173 should be changed so that image validation and image loading use the same location.

If the intended structure is that images are always stored under:

adata.uns["spatial"][library_id]["images"]

and spatial_key only specifies the coordinate system in adata.obsm, then line 166 could be changed from:

image_mapping = Key.uns.library_mapping(
    adata, spatial_key, Key.uns.image_key, library_id
)

to:

image_mapping = Key.uns.library_mapping(
    adata, Key.uns.spatial, Key.uns.image_key, library_id
)

Alternatively, if spatial_key is intended to specify both the coordinate system and the corresponding entry in adata.uns, then line 173 should probably use spatial_key instead of Key.uns.spatial.

Either way, making these two lookups consistent should resolve the issue when using custom spatial coordinate keys.

I'd be happy to submit a PR to fix this once the intended behavior of spatial_key is confirmed.

Versions

| Package  | Version   |
| -------- | --------- |
| tifffile | 2026.5.15 |
| scanpy   | 1.12.1    |
| squidpy  | 1.8.1     |
| anndata  | 0.12.16   |
| numpy    | 2.4.6     |

| Dependency               | Version                |
| ------------------------ | ---------------------- |
| stack_data               | 0.6.3                  |
| debugpy                  | 1.8.16                 |
| matplotlib-scalebar      | 0.9.0                  |
| spatialdata              | 0.7.3                  |
| zstandard                | 0.25.0                 |
| MarkupSafe               | 3.0.3                  |
| natsort                  | 8.4.0                  |
| llvmlite                 | 0.47.0                 |
| comm                     | 0.2.3                  |
| lz4                      | 4.4.5                  |
| parso                    | 0.8.7                  |
| rich                     | 15.0.0                 |
| shapely                  | 2.1.2                  |
| lazy-loader              | 0.5                    |
| more-itertools           | 11.1.0                 |
| Pygments                 | 2.20.0                 |
| fast-array-utils         | 1.4.1                  |
| cloudpickle              | 3.1.2                  |
| numcodecs                | 0.16.5                 |
| setuptools               | 82.0.1                 |
| tqdm                     | 4.67.3                 |
| cytoolz                  | 1.1.0                  |
| google-crc32c            | 1.8.0                  |
| python-dateutil          | 2.9.0.post0            |
| imagecodecs              | 2025.11.11             |
| xarray-dataclass         | 3.0.0                  |
| jupyter_core             | 5.9.1                  |
| legacy-api-wrap          | 1.5                    |
| packaging                | 26.2                   |
| matplotlib               | 3.10.9                 |
| pyproj                   | 3.7.2                  |
| zarr                     | 3.2.1                  |
| sparse                   | 0.18.0                 |
| tornado                  | 6.5.5                  |
| validators               | 0.35.0                 |
| numba                    | 0.65.1                 |
| h5py                     | 3.16.0                 |
| platformdirs             | 4.9.6                  |
| pathlib_abc              | 0.5.2                  |
| fsspec                   | 2026.4.0               |
| pillow                   | 12.2.0                 |
| xxhash                   | 3.7.0                  |
| seaborn                  | 0.13.2                 |
| threadpoolctl            | 3.6.0                  |
| dask                     | 2026.1.1               |
| scipy                    | 1.17.1                 |
| donfig                   | 0.8.1.post1            |
| wcwidth                  | 0.2.14                 |
| dask-image               | 2025.11.0              |
| xarray                   | 2026.4.0               |
| pandas                   | 2.3.3                  |
| pooch                    | 1.9.0 (v1.9.0)         |
| PyYAML                   | 6.0.3                  |
| traitlets                | 5.15.0                 |
| scikit-learn             | 1.8.0                  |
| pyarrow                  | 24.0.0                 |
| ipykernel                | 7.2.0                  |
| annsel                   | 0.1.2                  |
| spatial_image            | 1.2.3                  |
| pyzmq                    | 27.1.0                 |
| networkx                 | 3.6.1                  |
| multiscale_spatial_image | 2.0.3                  |
| six                      | 1.17.0                 |
| pyparsing                | 3.3.2                  |
| decorator                | 5.2.1                  |
| narwhals                 | 2.21.2                 |
| session-info2            | 0.4.1                  |
| msgpack                  | 1.1.2                  |
| certifi                  | 2026.6.17 (2026.06.17) |
| geopandas                | 1.1.3                  |
| Jinja2                   | 3.1.6                  |
| charset-normalizer       | 3.4.7                  |
| pure_eval                | 0.2.3                  |
| toolz                    | 1.1.0                  |
| cycler                   | 0.12.1                 |
| psutil                   | 7.2.2                  |
| scverse-misc             | 0.0.7                  |
| jupyter_client           | 8.8.0                  |
| universal_pathlib        | 0.3.10                 |
| scikit-image             | 0.26.0                 |
| ome-zarr                 | 0.15.0                 |
| pytz                     | 2026.2                 |
| typing_extensions        | 4.15.0                 |
| tblib                    | 3.2.2                  |
| executing                | 2.2.1                  |
| ipywidgets               | 8.1.7                  |
| ImageIO                  | 2.37.3                 |
| kiwisolver               | 1.5.0                  |
| joblib                   | 1.5.3                  |
| prompt_toolkit           | 3.0.52                 |
| docrep                   | 0.3.2                  |
| statsmodels              | 0.14.6                 |
| jedi                     | 0.20.0                 |
| asttokens                | 3.0.1                  |
| patsy                    | 1.0.2                  |
| ipython                  | 9.11.0                 |

| Component | Info                                                                           |
| --------- | ------------------------------------------------------------------------------ |
| Python    | 3.12.12 | packaged by conda-forge | (main, Oct 13 2025, 14:34:15) [GCC 14.3.0] |
| OS        | Linux-6.8.0-137-generic-x86_64-with-glibc2.39                                  |
| Updated   | 2026-08-19 09:19                                                             |

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions