You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spatial_resample raises TypeError when spatial_size is None and spatial_rank is 1 #9068
spatial_resample passes lambda x: x >= 0 to fall_back_tuple, but that helper's default predicate is lambda x: x and x > 0, whose x and short-circuits on None. The override does not, so a None element raises TypeError instead of falling back to the default — which is the documented behaviour of fall_back_tuple, and its own docstring says so:
spatial_size reaches that line as None whenever the caller did not supply one and spatial_rank <= 1: the branch above it, elif spatial_size is None and spatial_rank > 1, is the only thing that replaces None, so with a rank of 1 the None survives into ensure_tuple(None) → (None,) → None >= 0.
File "monai/data/image_writer.py", line 604, in set_metadata
File "monai/data/image_writer.py", line 273, in resample_if_needed
File "monai/transforms/spatial/array.py", line 229, in __call__
File "monai/transforms/spatial/functional.py", line 159, in <lambda>
fall_back_tuple(ensure_tuple(spatial_size)[:spatial_rank], in_spatial_size, lambda x: x >= 0)
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
Instrumenting spatial_resample confirms it is entered with spatial_size=None and img.shape=(1, 3, 5).
Expected behavior
None means "no size given for this axis" and should fall back to the corresponding in_spatial_size entry, exactly as fall_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:
Reproduced on unmodified dev (c1240a2d4) in two independent environments:
Python 3.12 / torch 2.13.0 / numpy 2.x
fails
Python 3.10 / torch 2.11.0 (highest version CI tests) / numpy 2.2.6
fails identically
So it is not a new-dependency artifact. git log -L159,159 dates that line to #6068 (Feb 2023).
Environment
Ensuring you use the relevant python executable, please paste the output of:
MONAI version: 1.6.0rc1+58.gc1240a2d4
Python: 3.10 and 3.12 (both affected)
PyTorch: 2.11.0 and 2.13.0 (both affected)
numpy: 2.2.6
nibabel: 5.4.2
Additional context
The narrowest fix is to make the predicate None-safe at the call site, matching the helper's default:
lambdax: xisnotNoneandx>=0
Worth checking the other fall_back_tuple callers that pass an explicit func for the same hazard — any predicate that does not short-circuit on None inherits it.
Nice catch — the fix you already pinned in the issue body (lambda x: x is not None and x >= 0 at functional.py:159) matches fall_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 the spatial_rank == 1/spatial_size=None path and the writer round-trip from your repro. I'll also sweep the other fall_back_tuple call sites for the same non-short-circuiting-predicate hazard you flagged and report back what I find.
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_rank is 1 here: this looks like a regression from #8765 (spatial_ndim tracking in MetaTensor). In ImageWriter.resample_if_needed (monai/data/image_writer.py), the affine is set on data_arraybefore the channel dim is added:
data_array=convert_to_tensor(data_array, track_meta=True) # shape (2, 3), no channel yetdata_array.affine= ... # setter clamps spatial_ndim to ndim - 1 = 1output_array=resampler(data_array[None], ...) # now (1, 2, 3), but cached spatial_ndim stays 1
So spatial_resample sees spatial_rank == 1 for a 2D image (debug print inside spatial_resample: shape (1, 2, 3), affine (3, 3), get_spatial_ndim == 1), skips the compute_shape_offset branch, and reaches fall_back_tuple with None.
Bisect (test_nifti_rw.py -k "write_2d or write_3d"):
Consequence for the None-safe predicate alone: it removes the TypeError, but with spatial_rank == 1 no resampling happens, so the output keeps the input affine (test_write_2d then fails with affine 1.0 vs expected 1.4), and test_write_3d still 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_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.)
Activity
MDSALMANSHAMS commentedon Aug 27, 2026
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.fix(transforms): guard None spatial_size in spatial_resample (Project…
docs(transforms): update spatial_resample docstrings for None spatial…
docs(transforms): add Returns and Raises sections to spatial_resample…
fix(transforms): guard None spatial_size in spatial_resample (Project…
docs(transforms): update spatial_resample docstrings for None spatial…
docs(transforms): add Returns and Raises sections to spatial_resample…
markov12 commentedon Sep 12, 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: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):
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 passingtests/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 passingOther 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.)
fix(transforms): resolve TypeError when spatial_size is None (Project…