Short answer
Most "MultiPolygon problems" are one of three things: a type mismatch (a Polygon being loaded into a MultiPolygon table or vice versa), an invalid geometry (self-intersection, bad ring orientation, overlapping parts), or a surprise promotion where an operation splits one polygon into disjoint pieces. The fixes are well-defined: promote types with ogr2ogr -nlt PROMOTE_TO_MULTI, repair with ST_MakeValid / QGIS Fix Geometries / ogr2ogr -makevalid, and understand that clip/overlay/dissolve legitimately create MultiPolygons.
Below: the data model, the OGC validity rules being violated, three repair workflows with commands, and how to verify the repair did not silently change your data.
Polygon vs MultiPolygon, precisely
In the OGC Simple Features model:
- A Polygon is exactly one exterior ring (the outer boundary) plus zero or more interior rings (holes). The rings must not cross; holes must lie inside the exterior; the boundary must be a single connected loop.
- A MultiPolygon is a collection of one or more Polygons treated as one feature. The constituent polygons must not overlap and may touch only at a finite number of points (not along edges).
You need a MultiPolygon whenever a single feature is geographically disjoint: an island nation, a parcel split by a road, a vegetation class occurring in separate patches. You also need it whenever the container forces it — and many do. A PostGIS column typed geometry(MultiPolygon, 32633) rejects a plain Polygon. A GeoPackage or shapefile layer created as MultiPolygon behaves the same way. This single-type enforcement is the source of most import errors.
Why type mismatches happen on import
GIS layers are conventionally homogeneous: one declared geometry type per layer/table. When source data mixes POLYGON and MULTIPOLYGON features (common after editing, or merging datasets), loading into a single-typed target fails feature-by-feature with errors like "Geometry type Polygon does not match column type MultiPolygon" and GDAL may skip the offending features — leaving you with a partial dataset that looks like a success.
The robust fix is to promote everything to the multi-type at conversion time:
ogr2ogr -f GPKG out.gpkg input.shp \
-nlt PROMOTE_TO_MULTI \
-nln parcels
PROMOTE_TO_MULTI wraps every Polygon as a single-part MultiPolygon, so the layer becomes uniformly MultiPolygon and nothing is dropped. This is almost always the right default when targeting PostGIS, GeoPackage, or any MultiPolygon-typed sink. Going the other direction (MultiPolygon to Polygon) is lossy and only safe if you first confirm every feature is single-part — otherwise use Multipart to Singleparts and accept that one feature becomes many rows.
Why a single polygon becomes a MultiPolygon
This is usually not a bug. Clip, intersect, difference, and dissolve routinely split a feature into disjoint pieces — clip a polygon with a frame that cuts it in two, dissolve adjacent parcels that touch only at a corner — and the result must be a MultiPolygon to remain one feature. PostGIS functions like ST_Union and ST_Intersection return whatever geometry the math produces, so a previously single Polygon comes back MultiPolygon. The geometry is correct; only the type changed. If a downstream table demands Polygon, that is the constraint to relax (promote to multi), not a defect to "fix."
Invalid MultiPolygons: the OGC rules being broken
ST_IsValid flags geometries that violate Simple Features rules. The common offenders in polygons:
- Self-intersection — the ring crosses itself (a bowtie), or two parts of a MultiPolygon overlap.
- Ring self-touch / spikes — a degenerate vertex creating a zero-area sliver.
- Wrong ring orientation — though PostGIS does not require a fixed orientation for validity, many formats and consumers (GeoJSON RFC 7946, some renderers) expect exterior rings counter-clockwise and holes clockwise; wrong orientation produces fills inverted or holes rendered as solid.
- Holes outside the shell, or overlapping parts in a MultiPolygon.
Invalid geometries cause spatial operations to error, return wrong areas, or silently produce empty results, so repair before analysis.
Three repair workflows
PostGIS
-- find the problems
SELECT id, ST_IsValidReason(geom)
FROM parcels
WHERE NOT ST_IsValid(geom);
-- repair in place
UPDATE parcels
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);
ST_MakeValid rebuilds a valid geometry as close as possible to the original; on PostGIS 3.2+ you can pass 'method=structure' to prefer area-preserving structural repair over the default linework method. Because repair can change geometry type (a self-intersecting polygon may become a MultiPolygon), keep the column multi-typed or apply ST_Multi():
UPDATE parcels SET geom = ST_Multi(ST_MakeValid(geom));
QGIS
Use Vector geometry > Fix geometries (native:fixgeometries) or Check Validity (qgis:checkvalidity) to get a layer of error locations you can inspect. Fix Geometries applies a make-valid pass and writes a clean output layer.
GDAL/ogr2ogr in one pass
ogr2ogr -f GPKG clean.gpkg dirty.gpkg \
-makevalid -nlt PROMOTE_TO_MULTI
This validates and promotes in a single conversion — convenient for delivery pipelines.
Worked example
A delivered shapefile of management units fails to load into geometry(MultiPolygon, 25831) and a colleague reports "wrong areas." Diagnosis and fix:
- Inspect:
ogrinfo -so dirty.shp layershows geometry type Polygon with some multipart features — a mixed/mismatched layer. - Load with promotion and validation:
ogr2ogr -f PostgreSQL PG:"dbname=geo" dirty.shp -nlt PROMOTE_TO_MULTI -makevalid -lco GEOMETRY_NAME=geom -a_srs EPSG:25831 -nln units - Confirm validity:
SELECT count(*) FROM units WHERE NOT ST_IsValid(geom);returns 0. - Check area drift: compare
SUM(ST_Area(geom))before (from a validated copy) and after; large changes flag features where make-valid removed self-overlaps — review those individually rather than trusting the total.
Common pitfalls and why they happen
- Silent feature dropping on import. Mixed types into a single-typed sink; GDAL skips mismatches. Use
PROMOTE_TO_MULTI. - "Fixed" geometry with changed area.
ST_MakeValidremoved a self-overlap, which legitimately alters area; treat unexpected drift as a review trigger, not noise. - Holes filled or inverted on a web map. Ring orientation does not match the consumer's expectation (notably GeoJSON RFC 7946). Normalize orientation, e.g.
ST_ForcePolygonCCWfor the exterior convention required. - CRS lost during conversion. A missing source CRS makes downstream area/validity meaningless; always confirm
-a_srs/-s_srsand that a.prjaccompanied the shapefile. - Treating promotion as corruption. A single polygon returning as MultiPolygon after dissolve/clip is correct; relax the type constraint instead of forcing single-part.
QA and validation
ST_IsValidcount = 0 after repair, withST_IsValidReasonreviewed for any stragglers.- Feature count preserved through conversion (no silent drops): compare
ogrinfo -socounts in and out. - Area sanity: total area within tolerance, and per-feature drift reviewed where make-valid acted.
- Type uniform: the output layer reports a single geometry type matching the target column.
- Render check: holes display as holes, multipart features select as one feature, on both desktop and the web target.
Bathyl perspective
Polygon validity is the quiet gate every spatial analysis passes through: an invalid ring or a dropped feature poisons every area, overlay, and tile downstream. Bathyl promotes to multi-type and runs make-valid as a standard ingest pass, then treats any area drift or feature-count change as a flag to inspect rather than a number to accept — so the dataset that opens cleanly is also the dataset that computes correctly.
Related reading
- Geometry Validity Explained
- Spatial Indexes in GIS Files
- Why Your GIS Layers Do Not Line Up
- Spatial data products