django/django · error · ValueError
Only numeric values of degree units are allowed on geographi
Error message
Only numeric values of degree units are allowed on geographic DWithin queries.
What it means
ValueError raised in get_distance (operations.py:140) when a Distance object is supplied to a dwithin lookup on a geodetic (lat/lon) SpatiaLite field. SpatiaLite's PtDistWithin on geographic data needs a plain degree value, not a Distance with metre/foot units.
Source
Thrown at django/contrib/gis/db/backends/spatialite/operations.py:140
def geo_db_type(self, f):
"""
Return None because geometry columns are added via the
`AddGeometryColumn` stored procedure on SpatiaLite.
"""
return None
def get_distance(self, f, value, lookup_type):
"""
Return the distance parameters for the given geometry field,
lookup value, and lookup type.
"""
if not value:
return []
value = value[0]
if isinstance(value, Distance):
if f.geodetic(self.connection):
if lookup_type == "dwithin":
raise ValueError(
"Only numeric values of degree units are allowed on "
"geographic DWithin queries."
)
dist_param = value.m
else:
dist_param = getattr(
value, Distance.unit_attname(f.units_name(self.connection))
)
else:
dist_param = value
return [dist_param]
def _get_spatialite_func(self, func):
"""
Helper routine for calling SpatiaLite functions and returning
their result.
Any error occurring in this method should be handled by the caller.
"""View on GitHub (pinned to ae25a40be0)
Solutions
- Pass a numeric degree value for geodetic dwithin: filter(geom__dwithin=(point, 0.05)) where 0.05 ≈ degrees.
- If you really need metre semantics, use a projected (non-geodetic) SRID for the field so Distance is accepted.
- On PostGIS, declare the field with geography=True to keep D(m=...) working.
- Convert metres to degrees at the call site (approximate: degrees = metres / 111320 for rough latitude).
Example fix
# before: distance object on geodetic spatialite qs = Shop.objects.filter(geom__dwithin=(pt, D(km=5))) # after: plain degree value qs = Shop.objects.filter(geom__dwithin=(pt, 0.045)) # ~5 km
Defensive patterns
Strategy: type-guard
Validate before calling
from django.contrib.gis.measure import Distance
from django.contrib.gis.geos import GEOSGeometry
def dwithin_value(field, point, dist):
if field.geodetic and isinstance(dist, Distance):
raise ValueError('geodetic dwithin needs a numeric degree value, not Distance')
return (point, dist) Type guard
from django.contrib.gis.measure import Distance
def is_distance_obj(v) -> bool:
return isinstance(v, Distance) Try / catch
try:
qs = Model.objects.filter(geom__dwithin=(pt, value))
except ValueError:
# value was a Distance on a geodetic field; fall back to a degree value
qs = Model.objects.filter(geom__dwithin=(pt, metres_to_degrees(value))) Prevention
- Detect geodetic fields and branch the lookup value type accordingly.
- Keep a helper metres_to_degrees(m, lat) for approximate conversions.
- Document per-backend behaviour of dwithin in the project's GIS README.
When it happens
Trigger: Calling Model.objects.filter(geom__dwithin=(point, D(km=5))) where geom has srid=4326 (geodetic) on the spatialite backend; the D(...) shortcut wraps the value as a Distance instance.
Common situations: Reusing the same query across PostGIS (geography) and SpatiaLite; copy-pasting a metre-based distance filter from a PostGIS tutorial into a SpatiaLite project; assuming D(m=...) works everywhere.
Related errors
- Only numeric values of degree units are allowed on geographi
- Perimeter cannot use a non-projected field.
- This backend does not support expressions for specifying dis
- Distance operations not available on this spatial backend.
- Area on geodetic coordinate systems not supported.
AI-assisted analysis of django/django@ae25a40be0 (2026-08-06).
Data as JSON: /api/errors/8b3a19324843381a.
Report an issue: GitHub.