{"record":{"id":"1e81d85c906a116b","repo":"elastic/elasticsearch","slug":"undefined-error-checking-for-neighbors","errorCode":null,"errorMessage":"Undefined error checking for neighbors","messagePattern":"Undefined error checking for neighbors","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/h3/src/main/java/org/elasticsearch/h3/HexRing.java","lineNumber":638,"sourceCode":"            if (originParent == destinationParent) {\n                int originResDigit = H3Index.H3_get_index_digit(origin, resolution);\n                int destinationResDigit = H3Index.H3_get_index_digit(destination, resolution);\n                if (originResDigit == CoordIJK.Direction.CENTER_DIGIT.digit()\n                    || destinationResDigit == CoordIJK.Direction.CENTER_DIGIT.digit()) {\n                    return true;\n                }\n                if (originResDigit >= CoordIJK.Direction.INVALID_DIGIT.digit()) {\n                    // Prevent indexing off the end of the array below\n                    throw new IllegalArgumentException(\"\");\n                }\n                if ((originResDigit == CoordIJK.Direction.K_AXES_DIGIT.digit()\n                    || destinationResDigit == CoordIJK.Direction.K_AXES_DIGIT.digit()) && H3.isPentagon(originParent)) {\n                    // If these are invalid cells, fail rather than incorrectly\n                    // reporting neighbors. For pentagon cells that are actually\n                    // neighbors across the deleted subsequence, they will fail the\n                    // optimized check below, but they will be accepted by the\n                    // gridDisk check below that.\n                    throw new IllegalArgumentException(\"Undefined error checking for neighbors\");\n                }\n                // These sets are the relevant neighbors in the clockwise\n                // and counter-clockwise\n                if (NEIGHBORSETCLOCKWISE[originResDigit].digit() == destinationResDigit\n                    || NEIGHBORSETCOUNTERCLOCKWISE[originResDigit].digit() == destinationResDigit) {\n                    return true;\n                }\n            }\n        }\n        // Otherwise, we have to determine the neighbor relationship the \"hard\" way.\n        for (int i = 0; i < 6; i++) {\n            long neighbor = h3NeighborInDirection(origin, DIRECTIONS[i].digit());\n            if (neighbor != -1) {\n                // -1 is an expected case when trying to traverse off of\n                // pentagons.\n                if (destination == neighbor) {\n                    return true;\n                }","sourceCodeStart":620,"sourceCodeEnd":656,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/h3/src/main/java/org/elasticsearch/h3/HexRing.java#L620-L656","documentation":"Thrown by HexRing.areNeighbours during the optimized same-parent fast path when a resolution digit equals the K_AXES_DIGIT and the shared parent is a pentagon. The k-axis is the deleted direction on pentagons, so the lookup tables cannot decide adjacency; the method declines to give a possibly-wrong answer. Note a separate empty-message IllegalArgumentException at the same call site guards INVALID_DIGIT.","triggerScenarios":"Reached from H3.areNeighborCells when both cells share a pentagon parent at resolution > 1 and at least one has the k-axis (skipped) digit at that resolution. For genuinely adjacent cells across the deleted subsequence, the fallback gridDisk loop later would accept them, but the fast path throws first.","commonSituations":"This is largely an internal invariant condition triggered by specific pentagon-adjacency geometries; users feeding valid indexes can still hit it because the optimized path is taken before the robust fallback. It indicates an edge case the fast path does not handle.","solutions":["Wrap H3.areNeighborCells in a try/catch for IllegalArgumentException and, on this specific message, fall back to a geometry-based adjacency test (e.g. compare h3ToGeoBoundary / great-circle distance) or treat as not-neighbors per your domain.","Prefer using H3's gridDisk/k-ring based neighborhood computation if available, which handles pentagon edges without this fast-path failure.","Validate inputs with h3IsValid first to rule out corrupt indexes; if inputs are valid, this is a known pentagon-geometry limitation, not a caller bug."],"exampleFix":"// before\nboolean near = H3.areNeighborCells(a, b); // may throw on pentagon k-axis edge\n\n// after\nboolean near;\ntry {\n    near = H3.areNeighborCells(a, b);\n} catch (IllegalArgumentException e) {\n    // fast path cannot decide pentagon k-axis adjacency\n    near = geometryAdjacent(a, b); // your fallback\n}","handlingStrategy":"try-catch","validationCode":"// Cannot fully pre-validate this geometric edge case; input can be valid yet trigger it.\n// Best prevention is wrapping the call and using a fallback adjacency test.\\nstatic boolean robustNeighbors(long a, long b) {\n    if (!org.elasticsearch.h3.H3.h3IsValid(a) || !org.elasticsearch.h3.H3.h3IsValid(b)) return false;\n    try {\n        return org.elasticsearch.h3.H3.areNeighborCells(a, b);\n    } catch (IllegalArgumentException e) {\n        return false; // or a geometry-based fallback\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return H3.areNeighborCells(a, b);\n} catch (IllegalArgumentException e) {\n    if (\"Undefined error checking for neighbors\".equals(e.getMessage())) {\n        // pentagon k-axis edge the fast path cannot decide\n        return geometryAdjacent(a, b); // your fallback, or false\n    }\n    throw e;\n}","preventionTips":["Treat areNeighborCells as fallible for pentagon-adjacency edge cases; wrap it.","If you need robust adjacency, compute via boundary/distance rather than the fast path.","Isolate the fallback so callers get a stable boolean contract."],"tags":["h3","neighbor","pentagon","internal"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}