Describe the bug
xrspatial.geotiff.to_geotiff (and the underlying write / write_streaming / GPU writers) silently writes a corrupt single-band TIFF when handed a 3D DataArray whose band axis has length 0.
_validate_writer_spatial_shape in xrspatial/geotiff/_validation.py validates the spatial height and width for 3D inputs, but does not check the band/sample dimension. A DataArray with shape (0, y, x) and dims ('band', 'y', 'x'), or shape (y, x, 0) with dims ('y', 'x', 'band'), passes validation. In _writer.py around line 1145, samples_per_pixel = first_arr.shape[2] if first_arr.ndim == 3 else 1 resolves to 0, and the file is assembled anyway around line 1198.
Reproduction
import numpy as np
import xarray as xr
from xrspatial.geotiff import to_geotiff, open_geotiff
# Band-first, zero bands
da1 = xr.DataArray(np.zeros((0, 5, 5), dtype="uint8"), dims=("band", "y", "x"))
to_geotiff(da1, "zero_bands_first.tif")
print(open_geotiff("zero_bands_first.tif").shape) # -> (5, 5) uint8
# Band-last, zero bands
da2 = xr.DataArray(np.zeros((5, 5, 0), dtype="uint8"), dims=("y", "x", "band"))
to_geotiff(da2, "zero_bands_last.tif")
print(open_geotiff("zero_bands_last.tif").shape) # -> (5, 5) uint8
Both calls succeed and the file round-trips as a (5, 5) single-band uint8 raster.
Expected behavior
The writer should fail closed at the public entry point with a clear message like "band/sample dimension must be positive; got shape (0, 5, 5) with 0 bands." No file should be written.
Why it matters
A clip, window, or selection that produces zero bands is a programmer error upstream. The writer treating that as "write a 2D raster of the same spatial size" is silent data fabrication. The on-disk file looks legitimate and reads back without an error from open_geotiff. Downstream consumers have no signal that the original band axis collapsed.
Scope of fix
- Extend
_validate_writer_spatial_shape (or add a peer validator) to reject bands == 0 on both layouts.
- Wire the new check into every public writer entry point that already calls the spatial validator:
to_geotiff / write / write_streaming and the GPU writer.
- Tests for both layouts on numpy and dask inputs.
Describe the bug
xrspatial.geotiff.to_geotiff(and the underlyingwrite/write_streaming/ GPU writers) silently writes a corrupt single-band TIFF when handed a 3D DataArray whose band axis has length 0._validate_writer_spatial_shapeinxrspatial/geotiff/_validation.pyvalidates the spatial height and width for 3D inputs, but does not check the band/sample dimension. A DataArray with shape(0, y, x)and dims('band', 'y', 'x'), or shape(y, x, 0)with dims('y', 'x', 'band'), passes validation. In_writer.pyaround line 1145,samples_per_pixel = first_arr.shape[2] if first_arr.ndim == 3 else 1resolves to 0, and the file is assembled anyway around line 1198.Reproduction
Both calls succeed and the file round-trips as a (5, 5) single-band uint8 raster.
Expected behavior
The writer should fail closed at the public entry point with a clear message like "band/sample dimension must be positive; got shape (0, 5, 5) with 0 bands." No file should be written.
Why it matters
A clip, window, or selection that produces zero bands is a programmer error upstream. The writer treating that as "write a 2D raster of the same spatial size" is silent data fabrication. The on-disk file looks legitimate and reads back without an error from
open_geotiff. Downstream consumers have no signal that the original band axis collapsed.Scope of fix
_validate_writer_spatial_shape(or add a peer validator) to rejectbands == 0on both layouts.to_geotiff/write/write_streamingand the GPU writer.