elastic/elasticsearch · error · IllegalArgumentException

Invalid cell: {}

Error message

Invalid cell: {}

What it means

Thrown by HexRing.areNeighbours (exposed via H3.areNeighborCells) when the ORIGIN index's mode field is not H3_CELL_MODE. H3 packs type information into the index; a non-cell-mode long is either an edge/ directed-edge index, a corrupt value, or a non-H3 long. The method refuses to reason about neighbors of a non-cell.

Source

Thrown at libs/h3/src/main/java/org/elasticsearch/h3/HexRing.java:594

    private static final CoordIJK.Direction[] NEIGHBORSETCOUNTERCLOCKWISE = new CoordIJK.Direction[] {
        CoordIJK.Direction.CENTER_DIGIT,
        CoordIJK.Direction.IK_AXES_DIGIT,
        CoordIJK.Direction.JK_AXES_DIGIT,
        CoordIJK.Direction.K_AXES_DIGIT,
        CoordIJK.Direction.IJ_AXES_DIGIT,
        CoordIJK.Direction.I_AXES_DIGIT,
        CoordIJK.Direction.J_AXES_DIGIT };

    /**
     * Returns whether or not the provided H3Indexes are neighbors.
     * @param origin The origin H3 index.
     * @param destination The destination H3 index.
     * @return true if the indexes are neighbors, false otherwise
     */
    public static boolean areNeighbours(long origin, long destination) {
        // Make sure they're hexagon indexes
        if (H3Index.H3_get_mode(origin) != Constants.H3_CELL_MODE) {
            throw new IllegalArgumentException("Invalid cell: " + origin);
        }

        if (H3Index.H3_get_mode(destination) != Constants.H3_CELL_MODE) {
            throw new IllegalArgumentException("Invalid cell: " + destination);
        }

        // Hexagons cannot be neighbors with themselves
        if (origin == destination) {
            return false;
        }

        final int resolution = H3Index.H3_get_resolution(origin);
        // Only hexagons in the same resolution can be neighbors
        if (resolution != H3Index.H3_get_resolution(destination)) {
            return false;
        }

        // H3 Indexes that share the same parent are very likely to be neighbors

View on GitHub (pinned to db6a809a66)

Solutions

  1. Gate inputs with H3.h3IsValid(origin) && H3.h3IsValid(destination) before calling areNeighborCells.
  2. If your pipeline includes edge indexes, keep cell and edge longs in distinct types/channels and never cross them.
  3. Re-derive indexes from coordinates via geoToH3 rather than trusting opaque longs from external sources.

Example fix

// before
boolean near = H3.areNeighborCells(maybeCell, other); // throws if maybeCell is an edge index

// after
boolean near = H3.h3IsValid(maybeCell) && H3.h3IsValid(other)
    && H3.areNeighborCells(maybeCell, other);
Defensive patterns

Strategy: validation

Validate before calling

static boolean safeNeighbors(long a, long b) {
    if (!org.elasticsearch.h3.H3.h3IsValid(a) || !org.elasticsearch.h3.H3.h3IsValid(b)) {
        return false; // or throw at the trust boundary
    }
    return org.elasticsearch.h3.H3.areNeighborCells(a, b);
}

Type guard

static boolean isCell(long h3) {
    return org.elasticsearch.h3.H3.h3IsValid(h3);
}

Try / catch

try {
    return H3.areNeighborCells(a, b);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Invalid cell:")) {
        // e.getMessage() includes the bad origin value
        return false;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling H3.areNeighborCells(a, b) where a (the origin) is not a cell-mode index: an edge index from a different H3 API, a corrupted long, or a value that was never a valid H3 cell. The destination is checked separately at [637].

Common situations: Intermixing cell indexes with edge indexes (H3 edge APIs return a different mode bit); reading indexes from an untrusted/external store without validation; bit-rot or endianness errors when serializing H3 longs across systems.

Related errors


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