elastic/elasticsearch · error · IllegalArgumentException

invalid child position

Error message

invalid child position

What it means

Thrown by H3.childPosToH3 when childPos is negative or exceeds the valid range for the cell's shape: 0..6 for hexagons (center + 6 edges) and 0..5 for pentagons (center + 5 edges, since the k-axis digit is skipped). The position must index into the cell's finite child set.

Source

Thrown at libs/h3/src/main/java/org/elasticsearch/h3/H3.java:273

        return h3ToStringList(h3ToChildren(stringToH3(h3Address)));
    }

    /**
     * Returns the child cell at the given position
     */
    public static long childPosToH3(long h3, int childPos) {
        final int childrenRes = H3Index.H3_get_resolution(h3) + 1;
        if (childrenRes > MAX_H3_RES) {
            throw new IllegalArgumentException("Resolution overflow");
        }
        final long childH = H3Index.H3_set_resolution(h3, childrenRes);
        if (childPos == 0) {
            return H3Index.H3_set_index_digit(childH, childrenRes, CoordIJK.Direction.CENTER_DIGIT.digit());
        }
        final boolean isPentagon = isPentagon(h3);
        final int maxPos = isPentagon ? 5 : 6;
        if (childPos < 0 || childPos > maxPos) {
            throw new IllegalArgumentException("invalid child position");
        }
        if (isPentagon) {
            // Pentagon skip digit (position) is the number 1, therefore we add one
            // to the current position.
            return H3Index.H3_set_index_digit(childH, childrenRes, childPos + 1);
        } else {
            return H3Index.H3_set_index_digit(childH, childrenRes, childPos);
        }
    }

    /**
     * Returns the child address at the given position
     */
    public static String childPosToH3(String h3Address, int childPos) {
        return h3ToString(childPosToH3(stringToH3(h3Address), childPos));
    }

    private static final int[] PEN_INTERSECTING_CHILDREN_DIRECTIONS = new int[] { 3, 1, 6, 4, 2 };

View on GitHub (pinned to db6a809a66)

Solutions

  1. Bound the loop by H3.h3ToChildrenSize(cell) (returns 7 for hexagons, 6 for pentagons) rather than a hardcoded 7.
  2. If accepting arbitrary user positions, validate 0 <= pos < h3ToChildrenSize(cell) before calling.
  3. Remember pentagons have one fewer child position (the deleted k-axis); never assume uniform 7.

Example fix

// before
for (int i = 0; i <= 6; i++) { // wrong for pentagons
    long child = H3.childPosToH3(cell, i);
}

// after
for (int i = 0; i < H3.h3ToChildrenSize(cell); i++) {
    long child = H3.childPosToH3(cell, i);
}
Defensive patterns

Strategy: validation

Validate before calling

static long safeChildPos(long h3, int pos) {
    int max = org.elasticsearch.h3.H3.h3ToChildrenSize(h3); // 7 hex / 6 pentagon
    if (pos < 0 || pos >= max) {
        throw new IllegalArgumentException("pos " + pos + " out of [0," + max + ")");
    }
    return org.elasticsearch.h3.H3.childPosToH3(h3, pos);
}

Type guard

static boolean validChildPos(long h3, int pos) {
    return pos >= 0 && pos < org.elasticsearch.h3.H3.h3ToChildrenSize(h3);
}

Try / catch

try {
    return H3.childPosToH3(h3, pos);
} catch (IllegalArgumentException e) {
    if (e.getMessage().equals("invalid child position")) {
        throw new IndexOutOfBoundsException("childPos " + pos + " invalid for " + h3);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling childPosToH3(cell, n) with n out of the legal band for that cell. A pentagon parent restricts positions to 0..5, so passing 6 against a pentagon throws; a hexagon parent accepts up to 6.

Common situations: Assuming 7 children for every cell and passing position 6 against a pentagon; computing positions from a hexagon-sized loop applied to a pentagon; off-by-one indexing from a caller that uses 1-based positions.

Related errors


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