﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
37360	GEOSGeometry() raises unhandled KeyError on curve geometry WKT/WKB (CIRCULARSTRING, COMPOUNDCURVE, CURVEPOLYGON, MULTICURVE) with GEOS 3.13+	Harsh Parmar		"`GEOSGeometry.__init__` dispatches a successfully-parsed GEOS geometry
pointer to a Python wrapper class using a hardcoded lookup table
(`GEOSGeometryBase._GEOS_CLASSES` in django/contrib/gis/geos/geometry.py)
that only covers the seven classic OGC Simple Features types (0-7).

GEOS 3.13+ can parse curve geometries from WKT and WKB -- CIRCULARSTRING
(type id 8), COMPOUNDCURVE (9), CURVEPOLYGON (10), and MULTICURVE (11) --
successfully, returning a valid geometry pointer. Django's dispatch table
has no entry for ids 8-11, so the lookup raises an unhandled `KeyError`
instead of the `GEOSException`/`ValueError` that every calling site is
written to expect and catch.

This is confirmed unhandled at every real entry point I checked, because
none of their except clauses include KeyError:

* `GeometryField.to_python()` (django/contrib/gis/forms/fields.py) --
  catches `(GEOSException, ValueError, TypeError)`
* `BaseGeometryWidget.deserialize()` (django/contrib/gis/forms/widgets.py)
  -- catches `(GEOSException, GDALException, ValueError, TypeError)`
* `BaseSpatialField.get_prep_value()` (django/contrib/gis/db/models/fields.py)
  -- catches `(TypeError, ValueError)` and separately `(GEOSException,
  GDALException)`

So `GeometryField.clean(user_input)` -- the exact method any Form/ModelForm
calls during normal request validation -- crashes with a raw KeyError
instead of a ValidationError, for input that is completely valid,
standards-compliant WKT.

= Reproduction =

{{{#!python
import django
from django.conf import settings
settings.configure()
django.setup()

from django.contrib.gis.geos import GEOSGeometry
GEOSGeometry(""CIRCULARSTRING(0 0, 1 1, 2 0)"")
}}}

Traceback:
{{{
  File ""django/contrib/gis/geos/geometry.py"", line 65, in __init__
    cls = GEOSGeometryBase._GEOS_CLASSES[self.geom_typeid]
KeyError: 8
}}}

Same result (KeyError: 8) via the public form-validation API:

{{{#!python
from django.contrib.gis.forms.fields import GeometryField
GeometryField(required=False).clean(""CIRCULARSTRING(0 0, 1 1, 2 0)"")
}}}

Other curve types reproduce with their own type id: COMPOUNDCURVE -> 9,
CURVEPOLYGON -> 10, MULTICURVE -> 11. Ordinary (non-curve) WKT is
unaffected (`POINT(1 1)` etc. work as normal).

Independently reproduced via a clean `pip install` of Django straight from
GitHub main (commit 935edaa9) into a fresh virtualenv, confirming this is
not an artifact of a locally modified checkout:

{{{
$ pip install ""django @ git+https://github.com/django/django.git@main""
Successfully installed django-6.2.dev20260919162742
$ python3 -c ""
from django.conf import settings
import django; settings.configure(); django.setup()
from django.contrib.gis.geos import GEOSGeometry
GEOSGeometry('CIRCULARSTRING(0 0, 1 1, 2 0)')""
Traceback (most recent call last):
  ...
  File "".../site-packages/django/contrib/gis/geos/geometry.py"", line 65, in __init__
    cls = GEOSGeometryBase._GEOS_CLASSES[self.geom_typeid]
KeyError: 8
}}}

= Relation to #34406 =

#34406 added curve-geometry support, but only to the **GDAL** wrapper
(django/contrib/gis/gdal/geometries.py already has CircularString,
CompoundCurve, CurvePolygon, MultiCurve, MultiSurface classes, merged as
04adff9f). The **GEOS** wrapper (django/contrib/gis/geos/geometry.py) was
never updated to match, and there are no GEOS-side tests for any curve
type (tests/gis_tests/geos_tests/ has no CIRCULARSTRING references, versus
tests/gis_tests/gdal_tests/test_geom.py and tests/gis_tests/data/geometries.json
which do). This is why it wasn't caught: it's only reachable once GEOS's
*own* WKT/WKB reader gains curve support, which is a recent GEOS feature
(3.13+) that most test/CI environments haven't run against yet.

= Suggested fix =

Either add the five curve-geometry types to
`GEOSGeometryBase._GEOS_CLASSES` (mirroring the GDAL side), or, as a
minimal stopgap, catch `KeyError` in `GEOSGeometryBase.__init__` and
re-raise as `GEOSException` with a clear ""unsupported geometry type""
message so it fails the way callers already expect.

= AI disclosure =

This report was found and drafted with assistance from Claude Code
(Anthropic). Specifically: coverage-guided fuzzing (Google's atheris/
libFuzzer bindings) was used to fuzz GEOSGeometry() against random
WKT/WKB/GML input, which surfaced a crashing input; that crash was then
manually reduced to the minimal repro above and independently re-verified
from scratch (default Django settings, no fuzzing-harness artifacts) by
running the reproduction code shown here and inspecting the relevant
source (the except clauses listed above) directly, plus a fresh `pip
install` from GitHub main into a clean virtualenv. Confirmed this is not
a duplicate of #34406 or any other ticket via Trac's own search before
filing."	Bug	new	GIS	dev	Normal		geos curve-geometry circularstring compoundcurve keyerror	Harsh Parmar	Unreviewed	0	0	0	0	0	0
