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 neighborsView on GitHub (pinned to db6a809a66)
Solutions
- Gate inputs with H3.h3IsValid(origin) && H3.h3IsValid(destination) before calling areNeighborCells.
- If your pipeline includes edge indexes, keep cell and edge longs in distinct types/channels and never cross them.
- 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
- Validate both indexes with h3IsValid before adjacency calls.
- Keep cell and edge longs in separate types/channels.
- Re-derive indexes from geoToH3 rather than trusting opaque longs.
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
- Invalid base cell looking for neighbor
- Illegal base cell
- Input is a base cell
- invalid child position
- invalid ring position
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/43dfa21230be09e2.
Report an issue: GitHub.