apache / apache/sedona-db

Panic "index out of bounds" in i_overlay bind/solver.rs during ST_Buffer on a valid polygon with a degenerate interior ring

Open
#1,178 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.