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
- Lowercase and trim the geohash before encoding: `hash = hash.trim().toLowerCase(Locale.ROOT);`
- Strip or reject the excluded vowels (a, i, l, o) — common OCR/copy-paste substitutions for 0/1.
- 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
- Lowercase geohashes before encoding — the alphabet is case-sensitive.
- Strip whitespace and the excluded vowels (a, i, l, o) at ingestion.
- Guard against non-geohash strings (UUIDs, hex) being routed into geohash APIs.
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
- empty geohash
- max y cannot be less than min y
- only one z value is specified
- max x cannot be less than min x
- invalid latitude
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/e59a86293c9a3031.
Report an issue: GitHub.