{"record":{"id":"3f4adb3c8b143b0c","repo":"django/django","slug":"cannot-use-object-normalized-r-for-a-spatial-loo","errorCode":null,"errorMessage":"Cannot use object {normalized!r} for a spatial lookup parameter. If this is a raster, wrap it with GDALRaster() before using it in a lookup to enable writing or fetching.","messagePattern":"Cannot use object (.+?) for a spatial lookup parameter\\. If this is a raster, wrap it with GDALRaster\\(\\) before using it in a lookup to enable writing or fetching\\.","errorType":"exception","errorClass":"DisallowedRasterLookup","httpStatus":null,"severity":"warning","filePath":"django/contrib/gis/gdal/raster/source.py","lineNumber":261,"sourceCode":"        if isinstance(ds_input, Path):\n            ds_input = str(ds_input)\n        return ds_input\n\n    @classmethod\n    def check_raster_lookup_value(cls, ds_input):\n        \"\"\"\n        Raise DisallowedRasterLookup for values inappropriate in lookups:\n        - No dicts, which GDALRaster(write=False) might still write to.\n        - No strings or Paths, which might fetch over the virtual filesystem.\n        \"\"\"\n        normalized = cls._preprocess_input(ds_input)\n        if isinstance(normalized, (dict, str)):\n            msg = (\n                f\"Cannot use object {normalized!r} for a spatial lookup \"\n                \"parameter. If this is a raster, wrap it with GDALRaster() \"\n                \"before using it in a lookup to enable writing or fetching.\"\n            )\n            raise DisallowedRasterLookup(msg)\n\n    def _flush(self):\n        \"\"\"\n        Flush all data from memory into the source file if it exists.\n        The data that needs flushing are geotransforms, coordinate systems,\n        nodata_values and pixel values. This function will be called\n        automatically wherever it is needed.\n        \"\"\"\n        # Raise an Exception if the value is being changed in read mode.\n        if not self._write:\n            raise GDALException(\n                \"Raster needs to be opened in write mode to change values.\"\n            )\n        capi.flush_ds(self._ptr)\n\n    @property\n    def vsi_buffer(self):\n        if not (","sourceCodeStart":243,"sourceCodeEnd":279,"githubUrl":"https://github.com/django/django/blob/ae25a40be07e8a749edf526df37c93e59d4a22c9/django/contrib/gis/gdal/raster/source.py#L243-L279","documentation":"Raised by GDALRaster.check_raster_lookup_value (a classmethod invoked during ORM spatial lookups) when the lookup value, after preprocessing, is a dict or str. Dicts can force GDALRaster into write mode and strings can be interpreted as virtual-filesystem paths that fetch remote data, so Django refuses to coerce them automatically inside a lookup. The fix is to construct the GDALRaster explicitly before using it in the queryset filter. It raises DisallowedRasterLookup (subclass of SuspiciousOperation), not GDALException.","triggerScenarios":"Using Model.objects.filter(geom__contains=some_dict) or filter(field__lookup='/vsicurl/http://...') where the right-hand side is a dict or a path-like string. Any GIS lookup (e.g. __intersects, __contains, __distance) receiving a raw dict/string describing a raster rather than a GDALRaster instance.","commonSituations":"Constructing raster lookup parameters dynamically and forgetting the GDALRaster() wrapper; passing a JSON dict straight from request data into a raster lookup; security hardening catching attempts to read remote VSI paths in a lookup.","solutions":["Wrap the value: GDALRaster(the_dict_or_str) before passing it to the lookup","If you only have dict/string input, build the GDALRaster once and reuse the instance in the filter","Validate user-supplied raster parameters at the view boundary and reject raw dict/str before they reach the ORM"],"exampleFix":"// before\nqs = RasterModel.objects.filter(rast__contains=json_dict)\n// after\nraster = GDALRaster(json_dict)\nqs = RasterModel.objects.filter(rast__contains=raster)","handlingStrategy":"validation","validationCode":"from django.contrib.gis.gdal.raster.source import GDALRaster\n\ndef coerce_raster_lookup(value):\n    if isinstance(value, (dict, str)):\n        return GDALRaster(value)\n    return value","typeGuard":"def is_lookup_safe(value) -> bool:\n    return not isinstance(value, (dict, str))","tryCatchPattern":"from django.contrib.gis.gdal.raster.source import DisallowedRasterLookup\ntry:\n    qs = Model.objects.filter(rast__contains=raw)\nexcept DisallowedRasterLookup:\n    qs = Model.objects.filter(rast__contains=GDALRaster(raw))","preventionTips":["Always wrap dict/str raster params in GDALRaster() before ORM lookups","Treat DisallowedRasterLookup as a security signal - never silently coerce untrusted VSI paths","Validate request-supplied raster descriptors at the view boundary"],"tags":["django-gis","gdal","raster","orm-lookup","security","suspicious-operation"],"analyzedSha":"ae25a40be07e8a749edf526df37c93e59d4a22c9","analyzedAt":"2026-08-06T21:46:51.801Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}