elastic/elasticsearch · error · ParseException
maximum nested depth of
Error message
maximum nested depth of
What it means
writeWKBGeometryCollection (line 460) guards recursion depth: at line 474 it throws ParseException if depth >= WellKnownText.MAX_NESTED_DEPTH (1000, defined at WellKnownText.java:49). Each nested GEOMETRYCOLLECTION increments depth (passed as depth+1 at lines 479/485). This is a denial-of-service safeguard against pathologically or maliciously nested input that would otherwise unbounded-recursion the parser.
Source
Thrown at libs/geo/src/main/java/org/elasticsearch/geometry/utils/WellKnownBinary.java:475
}
}
private static void writeWKBGeometryCollection(
StreamTokenizer stream,
ByteArrayOutputStream out,
ByteBuffer scratch,
boolean coerce,
int depth,
boolean explicitZ,
GeometryValidator validator
) throws IOException, ParseException {
if (WellKnownText.nextEmptyOrOpen(stream).equals(WellKnownText.EMPTY)) {
writeInt(out, scratch, 7);
writeInt(out, scratch, 0);
return;
}
if (depth >= WellKnownText.MAX_NESTED_DEPTH) {
throw new ParseException("maximum nested depth of " + WellKnownText.MAX_NESTED_DEPTH + " exceeded", stream.lineno());
}
List<byte[]> subGeometries = new ArrayList<>();
ByteArrayOutputStream subOut = new ByteArrayOutputStream();
writeWKBGeometry(stream, subOut, scratch, coerce, depth + 1, validator);
byte[] subBytes = subOut.toByteArray();
subGeometries.add(subBytes);
boolean hasZ = wkbTypeHasZ(subBytes);
while (WellKnownText.nextCloserOrComma(stream).equals(WellKnownText.COMMA)) {
subOut = new ByteArrayOutputStream();
writeWKBGeometry(stream, subOut, scratch, coerce, depth + 1, validator);
subBytes = subOut.toByteArray();
subGeometries.add(subBytes);
if (wkbTypeHasZ(subBytes) != hasZ) {
throw new IllegalArgumentException("all elements of the collection should have the same number of dimension");
}
}
WellKnownText.checkZorMAttribute(explicitZ, hasZ);
writeInt(out, scratch, hasZ ? 1007 : 7);View on GitHub (pinned to db6a809a66)
Solutions
- Apply a much smaller client-side depth limit (e.g. 20-50) on untrusted WKT before passing to fromWKT, to fail fast and free the parser from the heavy work.
- Validate/cap GEOMETRYCOLLECTION nesting in your ingestion schema or ingest processor.
- If legitimately deep nesting is expected (rare), pre-flatten the collection before serialization.
- Catch ParseException and surface a 400 to the client rather than crashing the ingest path.
Example fix
// before
byte[] wkb = WellKnownBinary.fromWKT(deeplyNestedWkt, BO, false, v); // throws at depth 1000
// after — pre-check depth cheaply
if (countNesting(wkt, "GEOMETRYCOLLECTION") > 50) {
throw new IllegalArgumentException("nesting too deep");
}
byte[] wkb = WellKnownBinary.fromWKT(wkt, BO, false, v); Defensive patterns
Strategy: validation
Validate before calling
static final int CLIENT_MAX_DEPTH = 50;
int countCollectionNesting(String wkt) {
int d = 0, max = 0;
for (int i = 0; i < wkt.length(); i++) {
if (wkt.regionMatches(true, i, "GEOMETRYCOLLECTION", 0, 18)) { d++; max = Math.max(max, d); }
// crude: decrement on ')' is unreliable; better to parse
}
return max;
}
boolean depthWithinBudget(String wkt) { return countCollectionNesting(wkt) <= CLIENT_MAX_DEPTH; } Type guard
// no type guard — operates on raw WKT string before parsing
Try / catch
try {
return WellKnownBinary.fromWKT(wkt, bo, coerce, v);
} catch (ParseException e) {
if (e.getMessage().startsWith("maximum nested depth")) {
throw new IllegalArgumentException("input nesting exceeds client budget", e);
}
throw e;
} Prevention
- Apply a small client-side depth cap (e.g. 50) on untrusted WKT before fromWKT.
- Reject pathologically nested GEOMETRYCOLLECTIONs at the API boundary.
- Treat the library's 1000 cap as a DoS backstop, not a target.
When it happens
Trigger: Calling WellKnownBinary.fromWKT on a WKT string with GEOMETRYCOLLECTION nesting deeper than 1000 levels, e.g. 1001 layers of 'GEOMETRYCOLLECTION(GEOMETRYCOLLECTION(...(POINT(0 0))...))'. Generated/malicious input is the typical source.
Common situations: Accepting untrusted WKT input without a depth sanity check (web-facing ingestion endpoints). Bugs in data-generation code that recursively wraps collections. Adversarial payloads aimed at stack-overflowing the parser. Extremely unlikely from normal GIS data, where nesting rarely exceeds 2-3 levels.
Related errors
- Unknown geometry type:
- Empty POINT cannot be represented in WKB
- holes must have the same number of dimensions as the polygon
- all elements of the collection should have the same number o
- Empty CIRCLE cannot be represented in WKB
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/03b15d1093523a12.
Report an issue: GitHub.