Skip to content

[1.5.x regression] ST_Union_Agg + INSERT corrupts transaction ("Skipping beyond end of binary data") and fails rollback #828

Description

@skutterai

What happens?

ST_Union_Agg followed by an INSERT … SELECT into a table with a GEOMETRY column (then CHECKPOINT) intermittently corrupts the
transaction on a persistent on‑disk database, throwing:

Invalid Input Error: Skipping beyond end of binary data at position 16

The error then recurs during the automatic rollback, which escalates to a fatal, connection‑killing state:

FATAL Error: Failed to rollback transaction. Cannot continue operation.

It is non‑deterministic: running the exact same script on the exact same data fails at a different GROUP BY partition each time. There is ample free RAM and no disk spilling when it happens. This is a regression in 1.5.x — the identical script on the identical database file completes with no error on 1.4.4, but fails on 1.5.3 (same OS, hardware, data, client, and glibc malloc). Smaller per‑row geometries (e.g. writing the ~32M un‑aggregated input polygons) write fine; only the large aggregated multipolygons produced by
ST_Union_Agg trigger it.

To Reproduce

The failure occurs on a ~32 M‑row spatial dataset and is data‑dependent and intermittent, so I have not yet been able to distill a data‑independent minimal example (a synthetic generator producing ~6,900‑vertex unions did not trigger it). The exact, faithful reproducer:

  import duckdb

  # postcodes.duckdb (~7 GB) contains one table:
  #   voronoi_raw(postcode VARCHAR, region_code VARCHAR(2), voronoi_geom GEOMETRY)
  #   ~32.2M rows: a Voronoi tessellation of UK address points
  #   ~1.75M distinct postcodes spread across 11 region_code values
  con = duckdb.connect("postcodes.duckdb")
  con.execute("LOAD spatial;")
  con.execute("SET threads=4;")            # also fails at threads=23
  con.execute("SET memory_limit='31GB';")  # no disk spill occurs; RAM is not exhausted

  con.execute("""
      CREATE TABLE voronoi_unioned (
          postcode VARCHAR PRIMARY KEY,
          region_code VARCHAR(2),
          unioned_geom GEOMETRY,
          cell_count INTEGER,
          area_m2 DOUBLE
      )
  """)

  # Union the cells of each postcode into one geometry, one region per INSERT.
  for (region,) in con.execute(
      "SELECT DISTINCT region_code FROM voronoi_raw ORDER BY 1"
  ).fetchall():
      con.execute(f"""
          INSERT INTO voronoi_unioned
          SELECT postcode, region_code, unioned_geom, cell_count, ST_Area(unioned_geom)
          FROM (
              SELECT postcode, region_code,
                     ST_Union_Agg(voronoi_geom) AS unioned_geom,
                     COUNT(*) AS cell_count
              FROM voronoi_raw
              WHERE region_code = '{region}'
              GROUP BY postcode, region_code
          )
          WHERE unioned_geom IS NOT NULL
      """)
      con.execute("CHECKPOINT;")
      print(region, "ok")

Output (the partition at which it dies varies run‑to‑run, e.g. NE one run, WA the next):

EE ok
 EM ok
 LN ok
 duckdb.duckdb.FatalException: FATAL Error: Failed to rollback transaction. Cannot continue operation.
 Original Error: Invalid Input Error: Skipping beyond end of binary data at position 16
 Rollback Error: Invalid Input Error: Skipping beyond end of binary data at position 16

The same script against the same file on DuckDB 1.4.4 completes all 11 partitions with no error:

  EE ok
  EM ok
  LN ok
  NE ok
  NW ok
  SC ok
  SE ok
  SW ok
  WA ok
  WM ok
  YH ok

The failing operation is plain SQL (ST_Union_Agg + INSERT + CHECKPOINT), so it should reproduce from the DuckDB CLI as well; I have only reproduced it via the Python client
against this dataset and have not isolated a data‑independent CLI script.

OS:

Ubuntu 24.04.4 LTS (Linux 6.17.0‑35‑generic, x86_64)

DuckDB Version:

1.5.3 (regression); 1.4.4 works correctly

DuckDB Client:

Python client (duckdb PyPI wheel), Python 3.12.3; spatial extension b68b309

Hardware:

AMD Ryzen 9 3900X (12C/24T), 62 GiB RAM (~49 GiB free during the run), NVMe SSD

Full Name:

Matt Duggan

Affiliation:

skutter.ai

Did you include all relevant configuration (e.g., CPU architecture, Linux distribution) to reproduce the issue?

  • Yes, I have

Did you include all code required to reproduce the issue?

  • Yes, I have

Did you include all relevant data sets for reproducing the issue?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions