{"record":{"id":"7a66630af946ae2b","repo":"elastic/elasticsearch","slug":"expected-a-polygon-got","errorCode":null,"errorMessage":"Expected a POLYGON, got [{}]","messagePattern":"Expected a POLYGON, got \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownBinary.java","lineNumber":771,"sourceCode":"            } else {\n                throw new IllegalArgumentException(\"Expected a \" + ShapeType.LINESTRING + \", got [\" + geometry.type() + \"]\");\n            }\n        }\n        return new MultiLine(Collections.unmodifiableList(lines));\n    }\n\n    private static MultiPolygon parseMultiPolygon(ByteBuffer byteBuffer, boolean coerce) {\n        final int numPolygons = byteBuffer.getInt();\n        if (numPolygons == 0) {\n            return MultiPolygon.EMPTY;\n        }\n        final List<Polygon> polygons = new ArrayList<>(numPolygons);\n        for (int i = 0; i < numPolygons; i++) {\n            final Geometry geometry = parseGeometry(byteBuffer, coerce);\n            if (geometry instanceof Polygon p) {\n                polygons.add(p);\n            } else {\n                throw new IllegalArgumentException(\"Expected a \" + ShapeType.POLYGON + \", got [\" + geometry.type() + \"]\");\n            }\n\n        }\n        return new MultiPolygon(Collections.unmodifiableList(polygons));\n    }\n\n    private static GeometryCollection<Geometry> parseGeometryCollection(ByteBuffer byteBuffer, boolean coerce) {\n        final int numShapes = byteBuffer.getInt();\n        if (numShapes == 0) {\n            return GeometryCollection.EMPTY;\n        }\n        final List<Geometry> shapes = new ArrayList<>(numShapes);\n        for (int i = 0; i < numShapes; i++) {\n            shapes.add(parseGeometry(byteBuffer, coerce));\n        }\n        return new GeometryCollection<>(shapes);\n    }\n","sourceCodeStart":753,"sourceCodeEnd":789,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownBinary.java#L753-L789","documentation":"parseMultiPolygon (line 760) reads the declared polygon count, then for each element calls parseGeometry (line 767) and checks instanceof Polygon (line 768). A non-Polygon sub-element triggers IllegalArgumentException at line 771. WKB MULTIPOLYGON (type 6/1006) must contain only Polygon elements.","triggerScenarios":"Calling WellKnownBinary.fromWKB with a MULTIPOLYGON WKB whose sub-elements are not Polygon-encoded. Causes: producer bugs allowing mixed-type multi-geometries, buffer corruption, endianness misalignment, or truncation that shifts the sub-element type-code read.","commonSituations":"Cross-system WKB exchange with emitters that mis-serialize MULTIPOLYGON. Misaligned offsets when slicing a larger buffer. Bit flips in transit/storage. Custom WKB emitters with incorrect sub-element type codes.","solutions":["Validate producer output against a reference WKB decoder.","Ensure MULTIPOLYGON sub-elements carry type code 3 (or 1003 for Z) if you control the producer.","Re-serialize from a trusted MultiPolygon via WellKnownBinary.toWKB to normalize.","Catch IllegalArgumentException and route the offending record to a dead-letter queue for inspection."],"exampleFix":"// before\nGeometry g = WellKnownBinary.fromWKB(v, false, corruptBytes); // throws: Expected a POLYGON, got [MULTIPOINT]\n\n// after — re-emit from trusted source\nPolygon p = new Polygon(new LinearRing(new double[]{0,1,1,0}, new double[]{0,0,1,0}));\nMultiPolygon mp = new MultiPolygon(List.of(p));\nbyte[] clean = WellKnownBinary.toWKB(mp, ByteOrder.LITTLE_ENDIAN);\nGeometry g = WellKnownBinary.fromWKB(v, false, clean);","handlingStrategy":"validation","validationCode":"// No cheap structural pre-check; normalize by re-emitting from trusted source.\nbyte[] normalize(MultiPolygon mp) { return WellKnownBinary.toWKB(mp, ByteOrder.LITTLE_ENDIAN); }","typeGuard":"// no useful type guard on raw bytes; validate via round-trip","tryCatchPattern":"try {\n    return WellKnownBinary.fromWKB(v, false, wkb);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Expected a POLYGON\")) {\n        // quarantine corrupt MULTIPOLYGON record\n    }\n    throw e;\n}","preventionTips":["Re-serialize MULTIPOLYGON from a trusted geometry object before exchange.","Validate producer output against a reference WKB decoder.","Ensure MULTIPOLYGON sub-elements carry type code 3 (or 1003 for Z)."],"tags":["geo","wkb","deserialization","multipolygon","type-mismatch","corrupt","elasticsearch"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T11:17:21.771Z"}