heibaiying/BigData-Notes · error · IllegalArgumentException

value structure should be longitude:latitude

Error message

value structure should be longitude:latitude

What it means

This IllegalArgumentException is thrown by the GEO branch of the Redis store bolt's process() logic (whitelisted from Storm's RedisStoreBolt) when the tuple value is not in the 'longitude:latitude' form required by Redis GEOADD. The code splits the value on ':' and expects exactly two parts, which are then parsed as doubles and passed to jedisCommand.geoadd(additionalKey, longitude, latitude, key). Any value without exactly one ':' separator fails before Redis is touched, is reported via collector.reportError(e), and the tuple is failed.

Source

Thrown at notes/Storm集成Redis详解.md:413

                    jedisCommand.hset(additionalKey, key, value);
                    break;

                case SET:
                    jedisCommand.sadd(key, value);
                    break;

                case SORTED_SET:
                    jedisCommand.zadd(additionalKey, Double.valueOf(value), key);
                    break;

                case HYPER_LOG_LOG:
                    jedisCommand.pfadd(key, value);
                    break;

                case GEO:
                    String[] array = value.split(":");
                    if (array.length != 2) {
                        throw new IllegalArgumentException("value structure should be longitude:latitude");
                    }

                    double longitude = Double.valueOf(array[0]);
                    double latitude = Double.valueOf(array[1]);
                    jedisCommand.geoadd(additionalKey, longitude, latitude, key);
                    break;

                default:
                    throw new IllegalArgumentException("Cannot process such data type: " + dataType);
            }

            collector.ack(input);
        } catch (Exception e) {
            this.collector.reportError(e);
            this.collector.fail(input);
        } finally {
            returnInstance(jedisCommand);
        }

View on GitHub (pinned to 3898939aca)

Solutions

  1. Fix the upstream producer so the value field is exactly "longitude:latitude" in decimal degrees, e.g. "116.407526:39.904030".
  2. If the upstream format is fixed and different, transform it before this bolt (add a preceding bolt that normalizes the value), or override process() to parse your format and call geoadd() yourself.
  3. If the data is not geographic at all, change the mapper's data type from GEO to the type that matches the data (STRING, HASH, SORTED_SET, ...).
  4. Sanitize in the mapper's getValueFromTuple(): validate and reformat there so bad values fail loudly at the boundary with a clearer message.

Example fix

// before: upstream spout emits
collector.emit(new Values(cityName)); // value="Beijing" -> array.length==1 -> throws

// after: upstream spout emits
collector.emit(new Values(cityName, "116.407526:39.904030")); // "longitude:latitude"
// (key=cityName, value="116.407526:39.904030" -> geoadd(additionalKey, 116.407526, 39.904030, cityName))
Defensive patterns

Strategy: validation

Validate before calling

String[] parts = value.split(":");
if (parts.length != 2 || !parts[0].matches("-?\\d+(\\.\\d+)?") || !parts[1].matches("-?\\d+(\\.\\d+)?")) {
    throw new IllegalArgumentException("Expected longitude:latitude, got: " + value);
}
// safe to emit into the GEO-typed bolt

Type guard

boolean isLongitudeLatitude(String value) {
    if (value == null) return false;
    String[] p = value.split(":");
    if (p.length != 2) return false;
    try {
        double lon = Double.parseDouble(p[0]);
        double lat = Double.parseDouble(p[1]);
        return lon >= -180 && lon <= 180 && lat >= -90 && lat <= 90;
    } catch (NumberFormatException e) {
        return false;
    }
}

Try / catch

// In a normalizing bolt upstream of the GEO store bolt:
try {
    collector.emit(new Values(key, lon + ":" + lat));
} catch (Exception e) {
    collector.reportError(e);
    collector.fail(input); // bad record: log and fail fast rather than retrying poisoned data
}

Prevention

When it happens

Trigger: Declaring RedisDataTypeDescription.RedisDataType.GEO in the store mapper while upstream tuples emit values that: contain no ':' (e.g. "beijing"), contain extra ':' segments (e.g. "116.40:39.90:0"), have empty parts (":39.90", "116.40:"), or use a different delimiter (comma, space, e.g. "116.40,39.90" — note Double.valueOf would also throw NumberFormatException afterwards for malformed numbers). Only exactly-two-part values reach geoadd().

Common situations: Feeding GEO-typed bolts from spouts that emit plain text (e.g. a word-count stream reused for a GEO demo); changing the upstream data contract (delimiter switched from ':' to ',' or to a POJO/JSON) without updating the bolt; locale/format issues where coordinates arrive as DMS strings ("39°54'N") instead of decimal degrees; test data copy-pasted with the wrong separator.

Related errors


AI-assisted analysis of heibaiying/BigData-Notes@3898939aca (2026-08-14). Data as JSON: /api/errors/f3f20c4127327607. Report an issue: GitHub.