django/django · error · ValueError
Band indices are not allowed for this operator, it works on…
Error message
Band indices are not allowed for this operator, it works on bbox only.
What it means
This ValueError is raised by PostGISOperator.check_raster() (django/contrib/gis/db/backends/postgis/operations.py:57-61) when a band index is provided for the left-hand side of a raster lookup but the operator has no SQL function (self.func is falsy). Operators without a func attribute work purely on bounding boxes (e.g., the &&, ~, @ operators for bboverlaps, bbcontains, contained) and PostGIS does not support per-band operations on bbox-only operators. The error fires when lhs_is_raster is True, band_lhs is an integer, but self.func is not set.
Solutions
- Remove the band index from bbox-only operators: use filter(rast__bbcontains=other_raster) instead of filter(rast__bbcontains=(other_raster, 1)).
- Use a function-based operator (contains, intersects, within) if per-band comparison is needed, as those have a func and support band indices.
- Check the operator definition in gis_operators to determine if it uses a func (supports bands) or only an op (bbox-only).
Example fix
# before qs = MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1)) # ValueError: Band indices are not allowed for this operator, it works on bbox only. # after # Remove band index for bbox operators qs = MyRasterModel.objects.filter(rast__bbcontains=other_raster) # Or use a function-based operator with band index qs = MyRasterModel.objects.filter(rast__contains=(other_raster, 1))
Defensive patterns
Strategy: validation
Validate before calling
BBOX_ONLY_RASTER_OPERATORS = {'bbcontains', 'bboverlaps', 'contained',
'overlaps_left', 'overlaps_right', 'overlaps_below',
'overlaps_above', 'left', 'right', 'strictly_below',
'strictly_above', 'same_as', 'exact'}
def allows_band_index(operator_name):
return operator_name not in BBOX_ONLY_RASTER_OPERATORS
# Use before adding band indices to a raster lookup:
# if allows_band_index('bbcontains'):
# qs = Model.objects.filter(rast__bbcontains=(raster, band)) # would work
# else:
# qs = Model.objects.filter(rast__bbcontains=raster) # no band Type guard
BBOX_ONLY_RASTER_OPERATORS = {'bbcontains', 'bboverlaps', 'contained',
'overlaps_left', 'overlaps_right', 'overlaps_below', 'overlaps_above',
'left', 'right', 'strictly_below', 'strictly_above', 'same_as', 'exact'}
def allows_band_index(operator_name):
return operator_name not in BBOX_ONLY_RASTER_OPERATORS Try / catch
try:
qs = MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1))
results = list(qs)
except ValueError as e:
if 'Band indices are not allowed' in str(e):
# Remove band index and retry
qs = MyRasterModel.objects.filter(rast__bbcontains=other_raster)
results = list(qs)
else:
raise Prevention
- Bbox operators (bbcontains, bboverlaps, contained) do not accept band indices.
- Only function-based operators (contains, intersects, within, etc.) support bands.
- Check operator.func before adding band parameters to raster lookups.
- When in doubt, omit the band index -- bbox comparison works on the whole raster.
When it happens
Trigger: Using a bounding-box raster operator (bbcontains, bboverlaps, contained) with a band index on a RasterField. For example: MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1)) — the bbcontains operator uses the MBR (~) operator which has no func, so band indices are meaningless. The band index parameter is rejected because bbox operators compare whole raster extents, not individual bands.
Common situations: Assuming all raster operators accept band indices when only function-based operators (ST_Contains, ST_Intersects, etc.) do. Copying a band-indexed lookup pattern from a function-based operator to a bbox operator. Programmatically applying band indices to all raster lookups without distinguishing operator types.
Related errors
- Band index must be an integer, but got
- Band pixeltypes are not all equal.
- Cannot determine PostGIS version for database
- Could not find a geometry or geography column for
- Could not open the datasource at
AI-assisted analysis of django/django@b5388a3a80 (2026-08-10).
Data as JSON: /api/errors/353a6ca0416f8f36.
Report an issue: GitHub.
Appendix: source
Thrown at django/contrib/gis/db/backends/postgis/operations.py:58
def as_sql(self, connection, lookup, template_params, *args):
template_params = self.check_raster(lookup, template_params)
template_params = self.check_geography(lookup, template_params)
return super().as_sql(connection, lookup, template_params, *args)
def check_raster(self, lookup, template_params):
spheroid = lookup.rhs_params and lookup.rhs_params[-1] == "spheroid"
# Check which input is a raster.
lhs_is_raster = lookup.lhs.field.geom_type == "RASTER"
rhs_is_raster = isinstance(lookup.rhs, GDALRaster)
# Look for band indices and inject them if provided.
if lookup.band_lhs is not None and lhs_is_raster:
if not isinstance(lookup.band_lhs, int):
name = lookup.band_lhs.__class__.__name__
raise TypeError(f"Band index must be an integer, but got {name!r}.")
if not self.func:
raise ValueError(
"Band indices are not allowed for this operator, it works on bbox "
"only."
)
template_params["lhs"] = "%s, %s" % (
template_params["lhs"],
lookup.band_lhs,
)
if lookup.band_rhs is not None and rhs_is_raster:
if not isinstance(lookup.band_rhs, int):
name = lookup.band_rhs.__class__.__name__
raise TypeError(f"Band index must be an integer, but got {name!r}.")
if not self.func:
raise ValueError(
"Band indices are not allowed for this operator, it works on bbox "
"only."
)
template_params["rhs"] = "%s, %s" % (View on GitHub (pinned to b5388a3a80)