Repository navigation
spatial_resample raises TypeError when spatial_size is None and spatial_rank is 1 #9068
Description
Activity
Nice catch — the fix you already pinned in the issue body (
lambda x: x is not None and x >= 0atfunctional.py:159) matchesfall_back_tuple's own default short-circuit exactly, so it's a drop-in. I'll put up a PR with that change plus regression tests covering thespatial_rank == 1/spatial_size=Nonepath and the writer round-trip from your repro. I'll also sweep the otherfall_back_tuplecall sites for the same non-short-circuiting-predicate hazard you flagged and report back what I find.- added 3 commits that reference this issue
on Aug 29, 2026 - added 3 commits that reference this issue
on Sep 5, 2026 Some extra findings on the root cause, in case they are useful for the fix already in progress (#9080). I'm not planning to open a competing PR.
Why
spatial_rankis 1 here: this looks like a regression from #8765 (spatial_ndimtracking inMetaTensor). InImageWriter.resample_if_needed(monai/data/image_writer.py), the affine is set ondata_arraybefore the channel dim is added:data_array = convert_to_tensor(data_array, track_meta=True) # shape (2, 3), no channel yet data_array.affine = ... # setter clamps spatial_ndim to ndim - 1 = 1 output_array = resampler(data_array[None], ...) # now (1, 2, 3), but cached spatial_ndim stays 1
So
spatial_resampleseesspatial_rank == 1for a 2D image (debug print insidespatial_resample:shape (1, 2, 3), affine(3, 3),get_spatial_ndim == 1), skips thecompute_shape_offsetbranch, and reachesfall_back_tuplewithNone.Bisect (
test_nifti_rw.py -k "write_2d or write_3d"):31419ab(parent of Add explicit spatial_ndim tracking to MetaTensor #8765): 2 passed26326b5(Add explicit spatial_ndim tracking to MetaTensor #8765): 2 failed (test_write_2dTypeError,test_write_3dassertion)
Consequence for the
None-safe predicate alone: it removes theTypeError, but withspatial_rank == 1no resampling happens, so the output keeps the input affine (test_write_2dthen fails with affine1.0vs expected1.4), andtest_write_3dstill fails (shape(1, 1, 5)vs expected(1, 1, 3)).Possible fix (add the channel dim before setting the affine):
- data_array = convert_to_tensor(data_array, track_meta=True) + # add the channel dim before setting the affine, so that the cached spatial_ndim is not clamped + data_array = convert_to_tensor(data_array, track_meta=True)[None] if affine is not None: data_array.affine = convert_to_tensor(affine, track_meta=False) # type: ignore resampler = SpatialResample(mode=mode, padding_mode=padding_mode, align_corners=align_corners, dtype=dtype) output_array = resampler( - data_array[None], dst_affine=target_affine, spatial_size=output_spatial_shape # type: ignore + data_array, dst_affine=target_affine, spatial_size=output_spatial_shape # type: ignore )
Results on current
dev(macOS arm64, Python 3.12.14, torch 2.14.0, nibabel 5.4.2, itk 5.4.7):test_nifti_rw.py: 39 passed / 2 failed → 41 passedtest_image_rw.py(TestLoadSaveNifti, 8 cases): all failing → all passing- whole
tests/data: 10 more tests pass, no new failures (the only remaining failure,test_lmdbdataset_dist, also fails before Add explicit spatial_ndim tracking to MetaTensor #8765 on my machine and looks macOS-multiprocessing related) test_save_image.py,test_save_imaged.py,test_itk_writer.py,test_png_rw.py: unchanged, all passing
Other code paths that set an affine on a tensor before adding a channel dim might be affected the same way, but I only checked the writer.
Happy for this to be folded into #9080 or used however is most convenient.
(Investigation done with the help of an AI assistant; I reviewed the results.)
- added a commit that references this issue
on Oct 3, 2026
Describe the bug
spatial_resamplepasseslambda x: x >= 0tofall_back_tuple, but that helper's default predicate islambda x: x and x > 0, whosex andshort-circuits onNone. The override does not, so aNoneelement raisesTypeErrorinstead of falling back to the default — which is the documented behaviour offall_back_tuple, and its own docstring says so:monai/transforms/spatial/functional.py:159:spatial_sizereaches that line asNonewhenever the caller did not supply one andspatial_rank <= 1: the branch above it,elif spatial_size is None and spatial_rank > 1, is the only thing that replacesNone, so with a rank of 1 theNonesurvives intoensure_tuple(None)→(None,)→None >= 0.To Reproduce
Instrumenting
spatial_resampleconfirms it is entered withspatial_size=Noneandimg.shape=(1, 3, 5).Expected behavior
Nonemeans "no size given for this axis" and should fall back to the correspondingin_spatial_sizeentry, exactly asfall_back_tuple's docstring describes.Screenshots / test impact
This is not a corner case reachable only by hand — it fails 8 tests on current
dev:tests/data/test_nifti_rw.py—test_write_2d,test_write_3dtests/data/test_image_rw.py— 4 failures + 4 errors acrossTestRegRes/ writer round-tripsReproduced on unmodified
dev(c1240a2d4) in two independent environments:So it is not a new-dependency artifact.
git log -L159,159dates that line to #6068 (Feb 2023).Environment
Ensuring you use the relevant python executable, please paste the output of:
Additional context
The narrowest fix is to make the predicate
None-safe at the call site, matching the helper's default:Worth checking the other
fall_back_tuplecallers that pass an explicitfuncfor the same hazard — any predicate that does not short-circuit onNoneinherits it.