elastic/elasticsearch · error · UnsupportedOperationException

Circle is not supported

Error message

Circle is not supported

What it means

SpatialEnvelopeVisitor computes the bounding box (envelope/MBR) of a Geometry by walking its points. Its visit(Circle) override unconditionally throws UnsupportedOperationException because converting a circle's radius (in meters) into x/y extents requires a Coordinate Reference System, which the visitor does not carry. The TODO at line 320 makes this an explicit, known limitation rather than a bug. Until CRS-aware circle expansion is implemented, no Circle — empty or not — can pass through this visitor.

Source

Thrown at libs/geo/src/main/java/org/elasticsearch/geometry/utils/SpatialEnvelopeVisitor.java:321

        }

        private static Rectangle maybeWrap(double top, double bottom, double negLeft, double negRight, double posLeft, double posRight) {
            double unwrappedWidth = posRight - negLeft;
            double wrappedWidth = 360 + negRight - posLeft;
            return unwrappedWidth <= wrappedWidth
                ? new Rectangle(negLeft, posRight, top, bottom)
                : new Rectangle(posLeft, negRight, top, bottom);
        }
    }

    private boolean isValid() {
        return pointVisitor.isValid();
    }

    @Override
    public Boolean visit(Circle circle) throws RuntimeException {
        // TODO: Support circle, if given CRS (needed for radius to x/y coordinate transformation)
        throw new UnsupportedOperationException("Circle is not supported");
    }

    @Override
    public Boolean visit(GeometryCollection<?> collection) throws RuntimeException {
        collection.forEach(geometry -> geometry.visit(this));
        return isValid();
    }

    @Override
    public Boolean visit(Line line) throws RuntimeException {
        for (int i = 0; i < line.length(); i++) {
            pointVisitor.visitPoint(line.getX(i), line.getY(i));
        }
        return isValid();
    }

    @Override
    public Boolean visit(LinearRing ring) throws RuntimeException {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Filter out or reject Circle geometries before calling the visitor: branch on geometry instanceof Circle (or check geometry.type() == ShapeType.CIRCLE) and skip envelope computation for those documents.
  2. If you need an envelope for a circle, expand the circle into a Polygon approximation (a bounded number of points around the circumference using the known CRS) before invoking the visitor.
  3. For GeometryCollection inputs, pre-flatten and drop any Circle children (or recurse manually skipping Circle) so the visitor's collection.forEach at line 326 never reaches visit(Circle).
  4. Wrap the call in try/catch(UnsupportedOperationException) only as a last resort — this error is deterministic, not transient, so prevention is preferable.

Example fix

// before
Optional<Rectangle> bbox = SpatialEnvelopeVisitor.visitGeo(geometry, WrapLongitude.WRAP);

// after
if (geometry instanceof Circle || containsCircle(geometry)) {
    return Optional.empty(); // circles unsupported by envelope visitor
}
Optional<Rectangle> bbox = SpatialEnvelopeVisitor.visitGeo(geometry, WrapLongitude.WRAP);
Defensive patterns

Strategy: type-guard

Validate before calling

boolean isEnvelopeSupported(Geometry g) {
    if (g instanceof Circle) return false;
    if (g instanceof GeometryCollection<?> c) {
        return c.stream().allMatch(isEnvelopeSupported::apply); // recurse
    }
    return true;
}

Type guard

static boolean isCircleOrContainsCircle(Geometry g) {
    if (g instanceof Circle) return true;
    if (g instanceof GeometryCollection<?> c) {
        for (Geometry child : c) if (isCircleOrContainsCircle(child)) return true;
    }
    return false;
}

Try / catch

try {
    Optional<Rectangle> bbox = SpatialEnvelopeVisitor.visitGeo(geometry, WrapLongitude.WRAP);
} catch (UnsupportedOperationException e) {
    // circle encountered — skip envelope for this document
    logger.warn("skipping envelope: circle unsupported", e);
}

Prevention

When it happens

Trigger: Calling SpatialEnvelopeVisitor.visitCartesian(circle), SpatialEnvelopeVisitor.visitGeo(circle, wrap), or geometry.visit(new SpatialEnvelopeVisitor(...)) where geometry is or contains (via GeometryCollection recursion at line 326) a Circle. Note that the collection visit forEach at line 326 will recurse into a Circle child and re-trigger this throw.

Common situations: Indexing geo_shape documents that use circle geometry and then running a query/aggregation that derives an envelope (e.g. for tile/bbox-based filtering, sorting, or _select_shape). Passing user-supplied or third-party GeoJSON/WKT that was parsed into a Circle directly to any code path that calls SpatialEnvelopeVisitor. Migrating data that previously stored circles into a query path that previously only handled polygons.

Related errors


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