elastic/elasticsearch · error · IllegalArgumentException

When specifying 'Z' or 'M', coordinates must include three v

Error message

When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided

What it means

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.

Source

Thrown at libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownText.java:470

            case "point" -> parsePoint(stream);
            case "multipoint" -> parseMultiPoint(stream);
            case "linestring" -> parseLine(stream);
            case "multilinestring" -> parseMultiLine(stream);
            case "polygon" -> parsePolygon(stream, coerce);
            case "multipolygon" -> parseMultiPolygon(stream, coerce);
            case "bbox" -> parseBBox(stream);
            case "geometrycollection" -> parseGeometryCollection(stream, coerce, depth + 1);
            case "circle" -> // Not part of the standard, but we need it for internal serialization
                parseCircle(stream);
            default -> throw new IllegalArgumentException("Unknown geometry type: " + type);
        };
        checkZorMAttribute(isExplicitlySpecifiesZorM, geometry.hasZ());
        return geometry;
    }

    static void checkZorMAttribute(boolean isExplicitlySpecifiesZorM, boolean hasZ) {
        if (isExplicitlySpecifiesZorM && hasZ == false) {
            throw new IllegalArgumentException(
                "When specifying 'Z' or 'M', coordinates must include three values. Only two coordinates were provided"
            );
        }
    }

    private static GeometryCollection<Geometry> parseGeometryCollection(StreamTokenizer stream, boolean coerce, int depth)
        throws IOException, ParseException {
        if (nextEmptyOrOpen(stream).equals(EMPTY)) {
            return GeometryCollection.EMPTY;
        }
        if (depth > MAX_NESTED_DEPTH) {
            throw new ParseException("maximum nested depth of " + MAX_NESTED_DEPTH + " exceeded", stream.lineno());
        }
        List<Geometry> shapes = new ArrayList<>();
        shapes.add(parseGeometry(stream, coerce, depth));
        while (nextCloserOrComma(stream).equals(COMMA)) {
            shapes.add(parseGeometry(stream, coerce, depth));
        }

View on GitHub (pinned to db6a809a66)

Solutions

  1. Either supply the third coordinate value for every tuple so geometry.hasZ() is true: "POINT Z (1 2 3)".
  2. Or remove the Z/M qualifier if the data is genuinely 2D: "POINT (1 2)".
  3. Ensure consistency: every coordinate tuple in the geometry must have the same dimensionality as the qualifier declares.

Example fix

// before: Z qualifier but only 2 coordinates
Geometry g = WellKnownText.fromWKT("POINT Z (1 2)");

// after: provide the Z value
Geometry g = WellKnownText.fromWKT("POINT Z (1 2 3)");
// or drop the qualifier for 2D data
Geometry g = WellKnownText.fromWKT("POINT (1 2)");
Defensive patterns

Strategy: validation

Validate before calling

// Ensure Z qualifier matches coordinate arity before parsing
static void validateZConsistency(String wkt) {
    String upper = wkt.toUpperCase(Locale.ROOT);
    boolean hasQualifier = upper.contains(" Z") || upper.contains(" Z(") || upper.contains("M(") || upper.matches("(?i).*\\bZ\\b.*\\(.*\\).*");
    if (hasQualifier) {
        // every coordinate tuple must have 3 components; do a quick count
        // (full validation is done by the parser, this is a pre-check)
    }
}

Try / catch

try {
    Geometry g = WellKnownText.fromWKT(wkt);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Z")) {
        // prompt user to add the third coordinate or drop the qualifier
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/6ed29665ffba1588. Report an issue: GitHub.