{"record":{"id":"8e8c6e7190c2b208","repo":"OtterMind/Chat2DB","slug":"ewkb-linear-ring-is-not-closed","errorCode":null,"errorMessage":"EWKB linear ring is not closed","messagePattern":"EWKB linear ring is not closed","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"info","filePath":"chat2db-community-server/chat2db-community-plugins/chat2db-community-postgresql/src/main/java/ai/chat2db/plugin/postgresql/value/sub/PostgreSQLGeometryProcessor.java","lineNumber":249,"sourceCode":"        int coordinateCount = readCount(cursor, byteOrder);\n        if (coordinateCount == 1) {\n            throw new IllegalArgumentException(\"EWKB line string must be empty or contain at least two coordinates\");\n        }\n        inspectFiniteCoordinates(cursor, byteOrder, coordinateCount, coordinateDimension);\n    }\n\n    private void inspectLinearRing(WkbCursor cursor, ByteOrder byteOrder, int coordinateDimension) {\n        int coordinateCount = readCount(cursor, byteOrder);\n        if (coordinateCount < 4) {\n            throw new IllegalArgumentException(\"EWKB linear ring must contain at least four coordinates\");\n        }\n\n        double[] firstCoordinate = readFiniteCoordinate(cursor, byteOrder, coordinateDimension);\n        inspectFiniteCoordinates(cursor, byteOrder, coordinateCount - 2, coordinateDimension);\n        double[] lastCoordinate = readFiniteCoordinate(cursor, byteOrder, coordinateDimension);\n        for (int i = 0; i < 2; i++) {\n            if (firstCoordinate[i] != lastCoordinate[i]) {\n                throw new IllegalArgumentException(\"EWKB linear ring is not closed\");\n            }\n        }\n    }\n\n    private void inspectFiniteCoordinates(WkbCursor cursor, ByteOrder byteOrder,\n                                          int coordinateCount, int coordinateDimension) {\n        for (int coordinate = 0; coordinate < coordinateCount; coordinate++) {\n            for (int ordinate = 0; ordinate < coordinateDimension; ordinate++) {\n                if (!Double.isFinite(cursor.readDouble(byteOrder))) {\n                    throw new IllegalArgumentException(\"EWKB coordinate is not finite\");\n                }\n            }\n        }\n    }\n\n    private double[] readFiniteCoordinate(WkbCursor cursor, ByteOrder byteOrder, int coordinateDimension) {\n        double[] coordinate = new double[coordinateDimension];\n        for (int i = 0; i < coordinateDimension; i++) {","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-plugins/chat2db-community-postgresql/src/main/java/ai/chat2db/plugin/postgresql/value/sub/PostgreSQLGeometryProcessor.java#L231-L267","documentation":"Thrown by PostgreSQLGeometryProcessor.inspectLinearRing when the first and last coordinates of a polygon ring do not match in their first two dimensions (X, Y). Per the OGC SFA specification, linear rings must be closed: the starting point must equal the ending point. This validation compares only the first two ordinates (indices 0 and 1) at lines 247-251. The exception is caught by convertJDBCValueByType at line 80 and the raw hex value is returned gracefully.","triggerScenarios":"Converting a PostgreSQL POLYGON geometry whose EWKB declares a ring with >=4 coordinates but where the first coordinate's X or Y differs from the last coordinate's X or Y. The comparison at line 248 checks firstCoordinate[i] != lastCoordinate[i] for i in {0,1}.","commonSituations":"Data where the closing point was omitted or mismatched; polygon rings serialized without the closing vertex; coordinate precision differences between systems that drop the closing point.","solutions":["No action required in production — convertJDBCValueByType catches this and returns the raw hex value","Repair the source data by appending the first coordinate as the last to close each ring","Use ST_MakeValid in PostgreSQL to automatically close rings where possible"],"exampleFix":"-- before: unclosed ring (first point != last point)\nSELECT ST_GeomFromEWKT('POLYGON((0 0, 1 0, 1 1, 0 1))');\n-- after: closed ring (first point == last point)\nSELECT ST_GeomFromEWKT('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))');","handlingStrategy":"try-catch","validationCode":"// Already caught in convertJDBCValueByType. For data-level validation:\n// SELECT gid, ST_IsValidDetail(geom) FROM my_table WHERE NOT ST_IsClosed(ST_ExteriorRing(geom));","typeGuard":null,"tryCatchPattern":"try {\n    inspectEwkb(ewkb);\n} catch (IllegalArgumentException e) {\n    log.debug(\"EWKB ring not closed\", e);\n    return value;\n}","preventionTips":["Always close polygon rings by appending the first point as the last","Use ST_IsClosed to verify rings before relying on the data","Run ST_MakeValid on imported geometries to auto-close rings"],"tags":["postgresql","ewkb","geometry","polygon","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}