{"record":{"id":"353a6ca0416f8f36","repo":"django/django","slug":"band-indices-are-not-allowed-for-this-operator-it","errorCode":null,"errorMessage":"Band indices are not allowed for this operator, it works on bbox only.","messagePattern":"Band indices are not allowed for this operator, it works on bbox only\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"django/contrib/gis/db/backends/postgis/operations.py","lineNumber":58,"sourceCode":"    def as_sql(self, connection, lookup, template_params, *args):\n        template_params = self.check_raster(lookup, template_params)\n        template_params = self.check_geography(lookup, template_params)\n        return super().as_sql(connection, lookup, template_params, *args)\n\n    def check_raster(self, lookup, template_params):\n        spheroid = lookup.rhs_params and lookup.rhs_params[-1] == \"spheroid\"\n\n        # Check which input is a raster.\n        lhs_is_raster = lookup.lhs.field.geom_type == \"RASTER\"\n        rhs_is_raster = isinstance(lookup.rhs, GDALRaster)\n\n        # Look for band indices and inject them if provided.\n        if lookup.band_lhs is not None and lhs_is_raster:\n            if not isinstance(lookup.band_lhs, int):\n                name = lookup.band_lhs.__class__.__name__\n                raise TypeError(f\"Band index must be an integer, but got {name!r}.\")\n            if not self.func:\n                raise ValueError(\n                    \"Band indices are not allowed for this operator, it works on bbox \"\n                    \"only.\"\n                )\n            template_params[\"lhs\"] = \"%s, %s\" % (\n                template_params[\"lhs\"],\n                lookup.band_lhs,\n            )\n\n        if lookup.band_rhs is not None and rhs_is_raster:\n            if not isinstance(lookup.band_rhs, int):\n                name = lookup.band_rhs.__class__.__name__\n                raise TypeError(f\"Band index must be an integer, but got {name!r}.\")\n            if not self.func:\n                raise ValueError(\n                    \"Band indices are not allowed for this operator, it works on bbox \"\n                    \"only.\"\n                )\n            template_params[\"rhs\"] = \"%s, %s\" % (","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/django/django/blob/b5388a3a80cafcce2e34196d8e81cf5b48eb33bb/django/contrib/gis/db/backends/postgis/operations.py#L40-L76","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\nqs = MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1))\n# ValueError: Band indices are not allowed for this operator, it works on bbox only.\n\n# after\n# Remove band index for bbox operators\nqs = MyRasterModel.objects.filter(rast__bbcontains=other_raster)\n# Or use a function-based operator with band index\nqs = MyRasterModel.objects.filter(rast__contains=(other_raster, 1))","handlingStrategy":"validation","validationCode":"BBOX_ONLY_RASTER_OPERATORS = {'bbcontains', 'bboverlaps', 'contained',\n                               'overlaps_left', 'overlaps_right', 'overlaps_below',\n                               'overlaps_above', 'left', 'right', 'strictly_below',\n                               'strictly_above', 'same_as', 'exact'}\n\ndef allows_band_index(operator_name):\n    return operator_name not in BBOX_ONLY_RASTER_OPERATORS\n\n# Use before adding band indices to a raster lookup:\n# if allows_band_index('bbcontains'):\n#     qs = Model.objects.filter(rast__bbcontains=(raster, band))  # would work\n# else:\n#     qs = Model.objects.filter(rast__bbcontains=raster)  # no band","typeGuard":"BBOX_ONLY_RASTER_OPERATORS = {'bbcontains', 'bboverlaps', 'contained',\n    'overlaps_left', 'overlaps_right', 'overlaps_below', 'overlaps_above',\n    'left', 'right', 'strictly_below', 'strictly_above', 'same_as', 'exact'}\n\ndef allows_band_index(operator_name):\n    return operator_name not in BBOX_ONLY_RASTER_OPERATORS","tryCatchPattern":"try:\n    qs = MyRasterModel.objects.filter(rast__bbcontains=(other_raster, 1))\n    results = list(qs)\nexcept ValueError as e:\n    if 'Band indices are not allowed' in str(e):\n        # Remove band index and retry\n        qs = MyRasterModel.objects.filter(rast__bbcontains=other_raster)\n        results = list(qs)\n    else:\n        raise","preventionTips":["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."],"tags":["gis","postgis","raster","bbox-operator","django"],"backgroundTag":null,"analyzedSha":"b5388a3a80cafcce2e34196d8e81cf5b48eb33bb","analyzedAt":"2026-08-10T17:37:52.993Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}