rainwoodman: Sure. I am thinking of only doing the first fix (int cast) in this PR, and then use another PR to change all instance type checks to `Integral` from 'int'.
mrocklin: > Sure. I am thinking of only doing the first fix (int cast) in this PR, and then use another PR to change all instance type checks to Integral from 'int'.

No objection from me.  I would like to include this PR in a bugfix release that goes out on Friday if possible.  My guess is that we'll get things in easily by that time though.
diff --git a/dask/array/slicing.py b/dask/array/slicing.py
index 964878798..4e170316f 100644
--- a/dask/array/slicing.py
+++ b/dask/array/slicing.py
@@ -391,7 +391,7 @@ def _slice_1d(dim_shape, lengths, index):
             ind = index - chunk_boundaries[i - 1]
         else:
             ind = index
-        return {i: ind}
+        return {int(i): int(ind)}
 
     assert isinstance(index, slice)
 

IndexError: tuple index out of range in optimization.py
Hi, I apologize but unfortunately I can't reproduce this issue without copy and pasting a lot of code. I was hoping that perhaps this would look familiar to somebody here and they might be able to point me toward a workaround.

If it looks like it's a more serious issue, I can try to isolate code to come up with a self-contained example.

```
     62 
     63         print("old_data.shape", old_data.shape, values_dset.shape)
---> 64         da.store(old_data, values_dset)
     65         # f_new['values_' + str(z)][:] = old_data
     66 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/core.py in store(sources, targets, lock, regions, compute, return_stored, **kwargs)
    959 
    960         if compute:
--> 961             result.compute(**kwargs)
    962             return None
    963         else:

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in compute(self, **kwargs)
    154         dask.base.compute
    155         """
--> 156         (result,) = compute(self, traverse=False, **kwargs)
    157         return result
    158 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in compute(*args, **kwargs)
    400     keys = [x.__dask_keys__() for x in collections]
    401     postcomputes = [x.__dask_postcompute__() for x in collections]
--> 402     results = schedule(dsk, keys, **kwargs)
    403     return repack([f(r, *a) for r, (f, a) in zip(results, postcomputes)])
    404 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/threaded.py in get(dsk, result, cache, num_workers, **kwargs)
     73     results = get_async(pool.apply_async, len(pool._pool), dsk, result,
     74                         cache=cache, get_id=_thread_get_id,
---> 75                         pack_exception=pack_exception, **kwargs)
     76 
     77     # Cleanup pools associated to dead threads

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/local.py in get_async(apply_async, num_workers, dsk, result, cache, get_id, rerun_exceptions_locally, pack_exception, raise_exception, callbacks, dumps, loads, **kwargs)
    519                         _execute_task(task, data)  # Re-execute locally
    520                     else:
--> 521                         raise_exception(exc, tb)
    522                 res, worker_id = loads(res_info)
    523                 state['cache'][key] = res

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/compatibility.py in reraise(exc, tb)
     67         if exc.__traceback__ is not tb:
     68             raise exc.with_traceback(tb)
---> 69         raise exc
     70 
     71 else:

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/local.py in execute_task(key, task_info, dumps, loads, get_id, pack_exception)
    288     try:
    289         task, data = loads(task_info)
--> 290         result = _execute_task(task, data)
    291         id = get_id()
    292         result = dumps((result, id))

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/local.py in _execute_task(arg, cache, dsk)
    269         func, args = arg[0], arg[1:]
    270         args2 = [_execute_task(a, cache) for a in args]
--> 271         return func(*args2)
    272     elif not ishashable(arg):
    273         return arg

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/core.py in getter_inline(a, b, asarray, lock)
    118     This inlining can be relevant to operations when running off of disk.
    119     """
--> 120     return getter(a, b, asarray=asarray, lock=lock)
    121 
    122 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/core.py in getter(a, b, asarray, lock)
     83         c = a[b]
     84         if asarray:
---> 85             c = np.asarray(c)
     86     finally:
     87         if lock:

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/numpy/core/numeric.py in asarray(a, dtype, order)
    499 
    500     """
--> 501     return array(a, dtype, copy=False, order=order)
    502 
    503 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/core.py in __array__(self, dtype, **kwargs)
   1207 
   1208     def __array__(self, dtype=None, **kwargs):
-> 1209         x = self.compute()
   1210         if dtype and x.dtype != dtype:
   1211             x = x.astype(dtype)

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in compute(self, **kwargs)
    154         dask.base.compute
    155         """
--> 156         (result,) = compute(self, traverse=False, **kwargs)
    157         return result
    158 

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in compute(*args, **kwargs)
    397                              collections=collections)
    398 
--> 399     dsk = collections_to_dsk(collections, optimize_graph, **kwargs)
    400     keys = [x.__dask_keys__() for x in collections]
    401     postcomputes = [x.__dask_postcompute__() for x in collections]

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in collections_to_dsk(collections, optimize_graph, **kwargs)
    192 
    193         dsk = merge(*(opt(dsk, keys, **kwargs)
--> 194                       for opt, (dsk, keys) in groups.items()))
    195     else:
    196         dsk, _ = _extract_graph_and_keys(collections)

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/base.py in <genexpr>(.0)
    192 
    193         dsk = merge(*(opt(dsk, keys, **kwargs)
--> 194                       for opt, (dsk, keys) in groups.items()))
    195     else:
    196         dsk, _ = _extract_graph_and_keys(collections)

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/optimization.py in optimize(dsk, keys, fuse_keys, fast_functions, inline_functions_fast_functions, rename_fused_keys, **kwargs)
     44     else:
     45         dsk4 = dsk3
---> 46     dsk5 = optimize_slices(dsk4)
     47 
     48     return dsk5

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/optimization.py in optimize_slices(dsk)
    132                     break
    133                 try:
--> 134                     c_index = fuse_slice(b_index, a_index)
    135                     # rely on fact that nested gets never decrease in
    136                     # strictness e.g. `(getter_nofancy, (getter, ...))` never

~/miniconda3/envs/cenv3/lib/python3.6/site-packages/dask/array/optimization.py in fuse_slice(a, b)
    280                 result.append(a[i])
    281                 continue
--> 282             while b[j] is None:  # insert any Nones on the rhs
    283                 result.append(None)
    284                 j += 1

IndexError: tuple index out of range
```

Python: 3.6.2
Dask: 0.18.1

diff --git a/dask/array/tests/test_optimization.py b/dask/array/tests/test_optimization.py
index 5ba01d564..63bcb2686 100644
--- a/dask/array/tests/test_optimization.py
+++ b/dask/array/tests/test_optimization.py
@@ -273,3 +273,15 @@ def test_turn_off_fusion():
 
     assert dask.get(a, y.__dask_keys__()) == dask.get(b, y.__dask_keys__())
     assert len(a) < len(b)
+
+
+def test_gh3937():
+    # test for github issue #3937
+    x = da.from_array([1, 2, 3.], (2,))
+    x = da.concatenate((x, [x[-1]]))
+    y = x.rechunk((2,))
+    # This will produce Integral type indices that are not ints (np.int64), failing
+    # the optimizer
+    y = da.coarsen(np.sum, y, {0: 2})
+    # How to trigger the optimizer explicitly?
+    y.compute()

pip install -e .[complete]
parse_log_pytest
pytest
pytest
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
click==8.0.4
cloudpickle==2.2.1
-e git+https://github.com/dask/dask.git@ab1e21ca589d9ccf0b5f4a5a83161f0e175ef58b#egg=dask
distributed==1.28.1
HeapDict==1.0.1
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
locket==1.0.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
msgpack==1.0.5
numpy==1.19.5
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pandas==1.1.5
partd==1.2.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
psutil==7.0.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
python-dateutil==2.9.0.post0
pytz==2025.2
PyYAML==6.0.1
six==1.17.0
sortedcontainers==2.4.0
tblib==1.7.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
toolz==0.12.0
tornado==6.1
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zict==2.1.0
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: dask
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - click==8.0.4
      - cloudpickle==2.2.1
      - distributed==1.28.1
      - heapdict==1.0.1
      - locket==1.0.0
      - msgpack==1.0.5
      - numpy==1.19.5
      - pandas==1.1.5
      - partd==1.2.0
      - psutil==7.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyyaml==6.0.1
      - six==1.17.0
      - sortedcontainers==2.4.0
      - tblib==1.7.0
      - toolz==0.12.0
      - tornado==6.1
      - zict==2.1.0
prefix: /opt/conda/envs/dask

dask/array/tests/test_optimization.py::test_gh3937
dask/array/tests/test_optimization.py::test_fuse_getitem dask/array/tests/test_optimization.py::test_fuse_getitem_lock dask/array/tests/test_optimization.py::test_optimize_with_getitem_fusion dask/array/tests/test_optimization.py::test_optimize_slicing dask/array/tests/test_optimization.py::test_fuse_slice dask/array/tests/test_optimization.py::test_fuse_slice_with_lists dask/array/tests/test_optimization.py::test_nonfusible_fancy_indexing dask/array/tests/test_optimization.py::test_hard_fuse_slice_cases dask/array/tests/test_optimization.py::test_dont_fuse_numpy_arrays dask/array/tests/test_optimization.py::test_minimize_data_transfer dask/array/tests/test_optimization.py::test_fuse_slices_with_alias dask/array/tests/test_optimization.py::test_dont_fuse_fancy_indexing_in_getter_nofancy dask/array/tests/test_optimization.py::test_fuse_getter_with_asarray[10] dask/array/tests/test_optimization.py::test_fuse_getter_with_asarray[5] dask/array/tests/test_optimization.py::test_fuse_getter_with_asarray[3] dask/array/tests/test_optimization.py::test_remove_no_op_slices_if_get_is_not_getter_or_getter_nofancy[getter-False] dask/array/tests/test_optimization.py::test_remove_no_op_slices_if_get_is_not_getter_or_getter_nofancy[getter_nofancy-False] dask/array/tests/test_optimization.py::test_remove_no_op_slices_if_get_is_not_getter_or_getter_nofancy[getitem-True] dask/array/tests/test_optimization.py::test_turn_off_fusion
dask/array/slicing.py
dask/array/slicing.py