{"record":{"id":"3cbce3027d0ef84e","repo":"elastic/elasticsearch","slug":"linearring-cannot-have-less-than-4-points","errorCode":null,"errorMessage":"LinearRing cannot have less than 4 points","messagePattern":"LinearRing cannot have less than 4 points","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/geo/src/main/java/org/elasticsearch/geometry/simplify/StreamingGeometrySimplifier.java","lineNumber":351,"sourceCode":"            this(maxPoints, calculator, null);\n        }\n\n        public PolygonSimplifier(int maxPoints, SimplificationErrorCalculator calculator, Monitor monitor) {\n            super(\"Polygon\", maxPoints, calculator, monitor);\n        }\n\n        @Override\n        public Polygon produce() {\n            return new Polygon(StreamingGeometrySimplifier.produceLinearRing(this));\n        }\n    }\n\n    static LinearRing produceLinearRing(StreamingGeometrySimplifier<?> simplifier) {\n        if (simplifier.length < 1) {\n            throw new IllegalArgumentException(\"No points have been consumed\");\n        }\n        if (simplifier.length < 4) {\n            throw new IllegalArgumentException(\"LinearRing cannot have less than 4 points\");\n        }\n        double[] x = new double[simplifier.length];\n        double[] y = new double[simplifier.length];\n        for (int i = 0; i < simplifier.length; i++) {\n            x[i] = simplifier.points[i].x;\n            y[i] = simplifier.points[i].y;\n        }\n        if (x[simplifier.length - 1] != x[0] || y[simplifier.length - 1] != y[0]) {\n            String inequality = \"(\" + x[0] + \" \" + y[0] + \") != (\" + x[simplifier.length - 1] + \" \" + y[simplifier.length - 1] + \")\";\n            throw new IllegalArgumentException(\"LinearRing cannot have first and last points differ: \" + inequality);\n        }\n        return new LinearRing(x, y);\n    }\n}\n","sourceCodeStart":333,"sourceCodeEnd":366,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/geo/src/main/java/org/elasticsearch/geometry/simplify/StreamingGeometrySimplifier.java#L333-L366","documentation":"Thrown by StreamingGeometrySimplifier.produceLinearRing when the simplifier has accumulated between 1 and 3 points. A LinearRing (per OGC/ESRI semantics used by Elasticsearch) requires at least 4 points, where the first and last are the same coordinate. Fewer than 4 cannot form a closed ring.","triggerScenarios":"Consuming 1, 2, or 3 points into a PolygonSimplifier and then calling produce(). This check fires after the length < 1 check, so it specifically catches the 'some points but not enough' case.","commonSituations":"Degenerate input polygons with only two or three distinct vertices; a bug in upstream geometry construction that produced a triangle ring without the closing duplicate; simplification aggressive enough to collapse a polygon below 4 points (the simplifier is supposed to enforce min points, but a misconfigured maxPoints or a custom code path can bypass that).","solutions":["Raise maxPoints so the simplifier cannot collapse the ring below 4 vertices.","Validate the source polygon has >= 4 vertices before simplifying; reject or pass through degenerate inputs.","If you are constructing the simplifier by hand, ensure at least 4 consume() calls (with first == last) before produce()."],"exampleFix":"// before\nvar s = new StreamingGeometrySimplifier.PolygonSimplifier(3, calc, monitor); // maxPoints too low\nfor (double[] pt : pts) s.consume(pt[0], pt[1]);\nPolygon p = s.produce(); // can collapse to < 4\n\n// after\nvar s = new StreamingGeometrySimplifier.PolygonSimplifier(Math.max(4, desiredMax), calc, monitor);\nfor (double[] pt : pts) s.consume(pt[0], pt[1]);\nPolygon p = s.length >= 4 ? s.produce() : preserveOriginal();","handlingStrategy":"validation","validationCode":"int maxPoints = Math.max(4, desiredMax);\nif (sourceRing.length < 4) throw new IllegalArgumentException(\"source ring has fewer than 4 points\");\nvar s = new StreamingGeometrySimplifier.PolygonSimplifier(maxPoints, calc, monitor);\nfor (int i = 0; i < sourceRing.length; i++) s.consume(sourceRing[i][0], sourceRing[i][1]);\nreturn s.produce();","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never set maxPoints below 4 for a PolygonSimplifier.","Reject source polygons with fewer than 4 vertices at ingestion.","If simplification collapses rings below 4, raise maxPoints or fall back to the original geometry."],"tags":["geometry","simplification","streaming","polygon","linear-ring","minimum-points","libs-geo"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}