Repository: pydata/xarray

Issue:
# Can not use map when grouping by multiple variables

### What happened?

Exception when using group by and map when grouping by multiple variables.


### What did you expect to happen?

_No response_

### Minimal Complete Verifiable Example

```Python
# /// script
# requires-python = ">=3.11"
# dependencies = [
#   "xarray[complete]@git+https://github.com/pydata/xarray.git@main",
# ]
# ///
#
# This script automatically imports the development branch of xarray to check for issues.
# Please delete this header if you have _not_ tested this script with `uv run`!

import xarray as xr

xr.show_versions()
d = xr.DataArray(
    [[0, 1], [2, 3]],
    coords={
        "lon": (["ny", "nx"], [[30, 40], [40, 50]]),
        "lat": (["ny", "nx"], [[10, 10], [20, 20]]),
    },
    dims=["ny", "nx"],
)

d.groupby(('lon', 'lat')).mean()  # works
d.groupby('lon').map(lambda x: x)  # works
d.groupby(('lon', 'lat')).map(lambda x: x)  # fails
#   File "xarray/core/nputils.py", line 93, in inverse_permutation
#     inverse_permutation[indices] = np.arange(len(indices), dtype=np.intp)
#     ~~~~~~~~~~~~~~~~~~~^^^^^^^^^
# IndexError: arrays used as indices must be of integer (or boolean) type
```

### Steps to reproduce

_No response_

### MVCE confirmation

- [x] Minimal example — the example is as focused as reasonably possible to demonstrate the underlying issue in xarray.
- [x] Complete example — the example is self-contained, including all data and the text of any traceback.
- [x] Verifiable example — the example copy & pastes into an IPython prompt or [Binder notebook](https://mybinder.org/v2/gh/pydata/xarray/main?urlpath=lab/tree/doc/examples/blank_template.ipynb), returning the result.
- [x] New issue — a search of GitHub Issues suggests this is not a duplicate.
- [x] Recent environment — the issue occurs with the latest version of xarray and its dependencies.

### Relevant log output

```Python

```

### Anything else we need to know?

_No response_

### Environment

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.12.11 | packaged by conda-forge | (main, Jun  4 2025, 14:38:53) [Clang 18.1.8 ]
python-bits: 64
OS: Darwin
OS-release: 25.1.0
machine: arm64
processor: arm
byteorder: little
LC_ALL: None
LANG: C.UTF-8
LOCALE: ('C', 'UTF-8')
libhdf5: 1.14.6
libnetcdf: None
xarray: 2025.12.0
pandas: 2.3.3
numpy: 1.26.4
scipy: 1.16.1
netCDF4: None
pydap: None
h5netcdf: None
h5py: 3.14.0
zarr: 2.18.7
cftime: None
nc_time_axis: None
iris: None
bottleneck: None
dask: 2025.11.0
distributed: 2025.11.0
matplotlib: 3.10.3
cartopy: None
seaborn: 0.13.2
numbagg: None
fsspec: 2025.7.0
cupy: None
pint: 0.24.4
sparse: 0.17.0
flox: 0.10.7
numpy_groupies: 0.11.3
setuptools: 80.9.0
pip: 25.3
conda: None
pytest: 8.4.2
mypy: None
IPython: 9.6.0
sphinx: 8.1.3

</details>


Implementation patch:
diff --git a/xarray/core/groupby.py b/xarray/core/groupby.py
index 827c0a3588f..77ea1b341c9 100644
--- a/xarray/core/groupby.py
+++ b/xarray/core/groupby.py
@@ -173,11 +173,12 @@ def _inverse_permutation_indices(positions, N: int | None = None) -> np.ndarray
 
     if isinstance(positions[0], slice):
         positions = _consolidate_slices(positions)
-        if positions == slice(None):
+        if positions == [slice(None)] or positions == [slice(0, None)]:
             return None
         positions = [np.arange(sl.start, sl.stop, sl.step) for sl in positions]
-
-    newpositions = nputils.inverse_permutation(np.concatenate(positions), N)
+    newpositions = nputils.inverse_permutation(
+        np.concatenate(tuple(p for p in positions if len(p) > 0)), N
+    )
     return newpositions[newpositions != -1]
 
 
@@ -990,7 +991,7 @@ def _maybe_reindex(self, combined):
             if (has_missing_groups and index is not None) or (
                 len(self.groupers) > 1
                 and not isinstance(grouper.full_index, pd.RangeIndex)
-                and not index.index.equals(grouper.full_index)
+                and not (index is not None and index.index.equals(grouper.full_index))
             ):
                 indexers[grouper.name] = grouper.full_index
         if indexers:


Test patch:
diff --git a/xarray/tests/test_groupby.py b/xarray/tests/test_groupby.py
index c320931098a..547c3de4144 100644
--- a/xarray/tests/test_groupby.py
+++ b/xarray/tests/test_groupby.py
@@ -3847,6 +3847,20 @@ def test_groupby_bins_mean_time_series():
     assert ds_agged.time.dtype == np.dtype("datetime64[ns]")
 
 
+def test_groupby_multi_map():
+    # https://github.com/pydata/xarray/issues/11004
+    d = xr.DataArray(
+        [[0, 1], [2, 3]],
+        coords={
+            "lon": (["ny", "nx"], [[30, 40], [40, 50]]),
+            "lat": (["ny", "nx"], [[10, 10], [20, 20]]),
+        },
+        dims=["ny", "nx"],
+    )
+    xr.testing.assert_equal(d, d.groupby("lon").map(lambda x: x))
+    xr.testing.assert_equal(d, d.groupby(("lon", "lat")).map(lambda x: x))
+
+
 # TODO: Possible property tests to add to this module
 # 1. lambda x: x
 # 2. grouped-reduce on unique coords is identical to array