Panic "index out of bounds" in i_overlay bind/solver.rs during ST_Buffer on a valid polygon with a degenerate interior ring
- Dominant language
- Rust
- Stars
- 503
- Forks
- 61
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 91
Description
## Describe the bug
`ST_Buffer` panics inside `i_overlay`'s hole-binding solver when the input is a
**valid** polygon containing a **degenerate (near-zero-area) interior ring**, once the
buffer distance is large enough to consume that hole.
```
pyo3_runtime.PanicException: index out of bounds: the len is 1 but the index is 9223372036854775807
```
from
```
thread '' panicked at .../i_overlay-4.0.7/src/bind/solver.rs:91:33
```
`9223372036854775807` is `i64::MAX`, which suggests an uninitialised sentinel index
escaping rather than an ordinary off-by-one.
We ran into this issue performing some data processing on a real-world dataset of building footprints.
**The hole is required.** Buffering the same exterior ring with the interior ring
dropped succeeds at every distance tried. That is consistent with the panic site being
`bind/solver.rs`, the code binding holes to their parent contour.
GEOS buffers the identical geometry without complaint (shapely 2.1.2 / GEOS 3.14.1),
so this looks specific to i_overlay rather than a general geometry-engine limit.
Because it surfaces as a `PanicException` — which subclasses `BaseException`, not
`Exception` — it cannot be caught by ordinary Python error handling, so one bad row
takes down the whole process.
## To Reproduce
```python
import sedonadb
# Valid polygon: a thin triangle containing one degenerate sliver hole.
# ST_Area 330.0808, hole area ~0.096. Coordinates are in an arbitrary local frame,
# rounded to 4 decimal places.
WKT = (
"POLYGON ("
"(0 0, 208.1046 9.4235, 208.2609 6.2574, 0 0), "
"(103.4778 4.061, 103.476 4.0517, 103.4742 4.0415, 103.4706 4.0314, "
"103.4626 3.994, 103.4626 3.9847, 103.4608 3.9746, 103.459 3.9465, "
"104.5244 3.501, 104.5307 3.5096, 104.5477 3.522, 104.5558 3.5291, "
"104.5639 3.5376, 103.4778 4.061))"
)
sd = sedonadb.connect()
# The input is valid.
print("ST_IsValid:", sd.sql(
f"SELECT ST_IsValid(ST_GeomFromWKT('{WKT}'))"
).to_arrow_table().column(0)[0].as_py())
# Panics here.
sd.sql(f"SELECT ST_Buffer(ST_GeomFromWKT('{WKT}'), 10.0)").to_arrow_table()
print("no panic")
```
Output:
```
ST_IsValid: True
thread '' panicked at .../i_overlay-4.0.7/src/bind/solver.rs:91:33:
index out of bounds: the len is 1 but the index is 9223372036854775807
```
### Buffer distance dependence
| distance | with the hole | hole dropped |
| --- | --- | --- |
| 0.001 | OK (55 pts) | OK (36 pts) |
| 0.01 | OK (52 pts) | OK (36 pts) |
| 0.1 | OK (36 pts) | OK (36 pts) |
| 1 | OK (40 pts) | OK (36 pts) |
| 5 | OK (44 pts) | OK (36 pts) |
| 5.8 | OK (56 pts) | OK (36 pts) |
| **6 … 70** | **panic** | OK (36 pts) |
| 75 | OK (56 pts) | OK (36 pts) |
| 100 | OK (44 pts) | OK (36 pts) |
The panic window is `6 <= distance <= 70`, i.e. once the buffer is large enough to
consume the degenerate hole but before it is large enough to swallow the whole
20-unit-wide feature. The same exterior ring with the interior ring dropped never
panics at any distance.
## Expected behavior
`ST_Buffer` returns a buffered geometry, or raises a catchable error — rather than
panicking.
## What I checked
- Not invalid input — `ST_IsValid` is True.
- Not `ST_MakeValid` — the panic is strictly inside `ST_Buffer`. `ST_MakeValid` also
does **not** remove the degenerate hole, so it is not a workaround.
- Not a coordinate-system or transform issue — no transform is involved, and the panic
is unaffected by translation or uniform scaling.
- Not the buffer style — the 2-arg round form above is enough.
- Note that `bind/solver.rs` is unchanged between i_overlay 4.0.6 and 4.0.7, and the
0.4.1-rc0 tag locks the same 4.0.7.
## Environment
- sedonadb: **0.4.0** (PyPI), the latest published release
- i_overlay: 4.0.7
- Python: 3.12
- OS: osx-arm64
Contributor guide
Research direction
Reproduce the case through the ST_Buffer SQL entry point using the supplied Python script and geometry. Then inspect i_overlay-4.0.7/src/bind/solver.rs around the reported panic and trace how the degenerate hole is bound when the buffer consumes it. Done means ST_Buffer returns a geometry or a catchable error for the reproduction instead of panicking.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, rust, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 54/100