elastic/elasticsearch · error · IllegalArgumentException

unsupported symbol [

Error message

unsupported symbol [

What it means

Thrown by Geohash.mortonEncode(String) when a character in the geohash is not found in the base-32 alphabet. Elasticsearch geohashes use the standard base-32 set: digits 0-9 and lowercase letters bcdefghjkmnpqrstuvwxyz — notably excluding 'a', 'i', 'l', and 'o' to avoid confusion with digits. Uppercase letters also fail because indexOf is case-sensitive.

Source

Thrown at libs/geo/src/main/java/org/elasticsearch/geometry/utils/Geohash.java:332

            }
        }
        return (l << 4) | length;
    }

    /**
     * Encode to a morton long value from a given geohash string
     */
    public static long mortonEncode(final String hash) {
        if (hash.isEmpty()) {
            throw new IllegalArgumentException("empty geohash");
        }
        int level = 11;
        long b;
        long l = 0L;
        for (char c : hash.toCharArray()) {
            b = (long) (BASE_32_STRING.indexOf(c));
            if (b < 0) {
                throw new IllegalArgumentException("unsupported symbol [" + c + "] in geohash [" + hash + "]");
            }
            l |= (b << ((level-- * 5) + (MORTON_OFFSET - 2)));
            if (level < 0) {
                // We cannot handle more than 12 levels
                break;
            }
        }
        return BitUtil.flipFlop(l);
    }

    /** approximate width of geohash tile for a specific precision in degrees */
    public static double lonWidthInDegrees(int precision) {
        return precisionToLonWidth[precision];
    }

    /** approximate height of geohash tile for a specific precision in degrees */
    public static double latHeightInDegrees(int precision) {
        return precisionToLatHeight[precision];

View on GitHub (pinned to db6a809a66)

Solutions

  1. Lowercase and trim the geohash before encoding: `hash = hash.trim().toLowerCase(Locale.ROOT);`
  2. Strip or reject the excluded vowels (a, i, l, o) — common OCR/copy-paste substitutions for 0/1.
  3. Validate the string against the allowed alphabet before calling mortonEncode.

Example fix

// before
long m = Geohash.mortonEncode("ABCD1234"); // throws — uppercase + 'A' not in alphabet

// after
String clean = hash.trim().toLowerCase(Locale.ROOT)
    .replace("a", "").replace("i", "").replace("l", "").replace("o", "");
long m = Geohash.mortonEncode(clean);
Defensive patterns

Strategy: validation

Validate before calling

String clean = hash == null ? "" : hash.trim().toLowerCase(Locale.ROOT);
if (clean.chars().anyMatch(c -> "0123456789bcdefghjkmnpqrstuvwxyz".indexOf(c) < 0)) {
    throw new IllegalArgumentException("invalid geohash character in: " + hash);
}
return Geohash.mortonEncode(clean);

Type guard

static boolean isValidGeohashString(String hash) {
    if (hash == null || hash.isEmpty()) return false;
    String alphabet = "0123456789bcdefghjkmnpqrstuvwxyz";
    for (char c : hash.toCharArray()) {
        if (alphabet.indexOf(Character.toLowerCase(c)) < 0) return false;
    }
    return true;
}

Prevention

When it happens

Trigger: Calling `Geohash.mortonEncode(hash)` (or toPoint/toBoundingBox) where hash contains any character outside {0-9, b-z minus a/i/l/o}. The offending character is named in the message.

Common situations: Uppercase geohashes ('BCDE' instead of 'bcde'); typos including the excluded vowels a/i/l/o; non-geohash strings (UUIDs, hex strings) mistakenly passed as geohashes; mixed-case or whitespace-contaminated input.

Related errors


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