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
ValueError raised in PostGISOperator.check_raster() (line 57-61) when a left-hand-side band index is provided for a raster operator that has no func attribute — i.e., a bounding-box-only operator defined purely by an SQL op symbol (like ~, &&, @). These operators work on the raster's convex hull and cannot reference individual bands.
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 ae25a40be0)
Solutions
- Remove the band index from bbox-only lookups (bbcontains, bboverlaps, contained, left, right, strictly_above, etc.).
- If band-level comparison is needed, switch to a func-based operator like contains, intersects, or within that supports band indices.
- Consult gis_operators in PostGISOperations to confirm which operators accept raster band indices.
Example fix
// before — bbox operator with band index MyRasterModel.objects.filter(rast__bboverlaps=(other_rast, 1)) // ValueError: Band indices are not allowed for this operator, it works on bbox only. // after — drop the band index for bbox operators MyRasterModel.objects.filter(rast__bboverlaps=other_rast) // or use a func-based operator that supports bands: MyRasterModel.objects.filter(rast__contains=(other_rast, 1))
Defensive patterns
Strategy: validation
Validate before calling
from django.contrib.gis.db.backends.postgis.operations import PostGISOperations
BBOX_ONLY_OPS = {
name for name, op in PostGISOperations.gis_operators.items()
if not getattr(op, 'func', None)
}
def supports_band_index(lookup_name: str) -> bool:
return lookup_name not in BBOX_ONLY_OPS Type guard
def is_bbox_only_operator(lookup_name: str) -> bool:
op = PostGISOperations.gis_operators.get(lookup_name)
return op is not None and not getattr(op, 'func', None) Try / catch
try:
Model.objects.filter(rast__bboverlaps=(other, 1))
except ValueError:
# bbox operator — drop the band index
Model.objects.filter(rast__bboverlaps=other) Prevention
- Never pass band indices to bbox-only operators (bbcontains, bboverlaps, contained, left, right, etc.).
- Only func-based operators (contains, intersects, within, covers, dwithin) accept band indices.
- Centralize raster lookup construction in a helper that validates band-index usage per operator.
When it happens
Trigger: Using a bbox operator with a band index: MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1)) or rast__bboverlaps=(other, 1), where the operator (bbcontains, bboverlaps, contained, overlaps_left, etc.) has no func and the tuple includes a band index on the LHS.
Common situations: Assuming all raster operators accept band indices; copy-pasting a band index from a contains/intersects lookup into a bboverlaps lookup.
Related errors
- Band index must be an integer, but got {name!r}.
- Band pixeltypes are not all equal.
- Error implementing psycopg2 protocol. Is psycopg2 installed?
- Could not find a geometry or geography column for "%s"."%s"
- Cannot determine PostGIS version for database "%s" using com
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/353a6ca0416f8f36.
Report an issue: GitHub.