{"record":{"id":"6ed29665ffba1588","repo":"elastic/elasticsearch","slug":"when-specifying-z-or-m-coordinates-must-inclu","errorCode":null,"errorMessage":"When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided","messagePattern":"When specifying 'Z' or 'M', coordinates must include three values\\. Only two coordinates were provided","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownText.java","lineNumber":470,"sourceCode":"            case \"point\" -> parsePoint(stream);\n            case \"multipoint\" -> parseMultiPoint(stream);\n            case \"linestring\" -> parseLine(stream);\n            case \"multilinestring\" -> parseMultiLine(stream);\n            case \"polygon\" -> parsePolygon(stream, coerce);\n            case \"multipolygon\" -> parseMultiPolygon(stream, coerce);\n            case \"bbox\" -> parseBBox(stream);\n            case \"geometrycollection\" -> parseGeometryCollection(stream, coerce, depth + 1);\n            case \"circle\" -> // Not part of the standard, but we need it for internal serialization\n                parseCircle(stream);\n            default -> throw new IllegalArgumentException(\"Unknown geometry type: \" + type);\n        };\n        checkZorMAttribute(isExplicitlySpecifiesZorM, geometry.hasZ());\n        return geometry;\n    }\n\n    static void checkZorMAttribute(boolean isExplicitlySpecifiesZorM, boolean hasZ) {\n        if (isExplicitlySpecifiesZorM && hasZ == false) {\n            throw new IllegalArgumentException(\n                \"When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided\"\n            );\n        }\n    }\n\n    private static GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream, boolean coerce, int depth)\n        throws IOException, ParseException {\n        if (nextEmptyOrOpen(stream).equals(EMPTY)) {\n            return GeometryCollection.EMPTY;\n        }\n        if (depth > MAX_NESTED_DEPTH) {\n            throw new ParseException(\"maximum nested depth of \" + MAX_NESTED_DEPTH + \" exceeded\", stream.lineno());\n        }\n        List<Geometry> shapes = new ArrayList<>();\n        shapes.add(parseGeometry(stream, coerce, depth));\n        while (nextCloserOrComma(stream).equals(COMMA)) {\n            shapes.add(parseGeometry(stream, coerce, depth));\n        }","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownText.java#L452-L488","documentation":"Thrown by WellKnownText.checkZorMAttribute after a geometry is parsed. If the WKT explicitly specified a 'Z' or 'M' qualifier on the type keyword (e.g. \"POINT Z (...)\" or \"POINTM (...)\"), but the parsed geometry's hasZ() returns false (coordinates only had 2 values), the qualifier promised a third dimension that was not delivered.","triggerScenarios":"WKT input of the form \"POINT Z (1 2)\" or \"LINESTRING M (0 0, 1 1)\" where the qualifier declares Z/M but the coordinate tuples contain only two numbers. The parser reads the Z/M token, sets isExplicitlySpecifiesZorM=true, then parsePoint/parseLine reads only lon and lat (no third number), so geometry.hasZ() is false and checkZorMAttribute throws.","commonSituations":"Hand-authored or generated WKT where the Z qualifier was added for documentation but coordinates were left 2D; mixed-up serialization from a tool that emits the qualifier header but trims the third coordinate; copy-paste from a 3D dataset with the altitude column dropped.","solutions":["Either supply the third coordinate value for every tuple so geometry.hasZ() is true: \"POINT Z (1 2 3)\".","Or remove the Z/M qualifier if the data is genuinely 2D: \"POINT (1 2)\".","Ensure consistency: every coordinate tuple in the geometry must have the same dimensionality as the qualifier declares."],"exampleFix":"// before: Z qualifier but only 2 coordinates\nGeometry g = WellKnownText.fromWKT(\"POINT Z (1 2)\");\n\n// after: provide the Z value\nGeometry g = WellKnownText.fromWKT(\"POINT Z (1 2 3)\");\n// or drop the qualifier for 2D data\nGeometry g = WellKnownText.fromWKT(\"POINT (1 2)\");","handlingStrategy":"validation","validationCode":"// Ensure Z qualifier matches coordinate arity before parsing\nstatic void validateZConsistency(String wkt) {\n    String upper = wkt.toUpperCase(Locale.ROOT);\n    boolean hasQualifier = upper.contains(\" Z\") || upper.contains(\" Z(\") || upper.contains(\"M(\") || upper.matches(\"(?i).*\\\\bZ\\\\b.*\\\\(.*\\\\).*\");\n    if (hasQualifier) {\n        // every coordinate tuple must have 3 components; do a quick count\n        // (full validation is done by the parser, this is a pre-check)\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    Geometry g = WellKnownText.fromWKT(wkt);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Z\")) {\n        // prompt user to add the third coordinate or drop the qualifier\n    }\n    throw e;\n}","preventionTips":["Keep Z/M qualifiers consistent with the coordinate arity in source data.","If the dataset is 2D, omit the qualifier entirely.","Normalize source tuples to uniform dimensionality before WKT generation."],"tags":["geo","wkt","parsing","elasticsearch","geometry","validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}