grpc/grpc-java · error · ClassCastException

Number expected to be long:

Error message

Number expected to be long: 

What it means

getNumberAsLong casts a Double config value to long. If the fractional part is non-zero (or the double is outside exact long representation), the cast would lose data, so a ClassCastException with 'Number expected to be long: ' is thrown. It guards long-typed channel/service-config fields.

Source

Thrown at core/src/main/java/io/grpc/internal/JsonUtil.java:170

        String.format("value '%s' for key '%s' is not an integer", value, key));
  }

  /**
   * Gets a number from an object for the given key, casted to an long.  If the key is not
   * present, this returns null.  If the value does not represent a long integer, throws an
   * exception.
   */
  public static Long getNumberAsLong(Map<String, ?> obj, String key) {
    assert key != null;
    if (!obj.containsKey(key)) {
      return null;
    }
    Object value = obj.get(key);
    if (value instanceof Double) {
      Double d = (Double) value;
      long l = d.longValue();
      if (l != d) {
        throw new ClassCastException("Number expected to be long: " + d);
      }
      return l;
    }
    if (value instanceof String) {
      try {
        return Long.parseLong((String) value);
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException(
            String.format("value '%s' for key '%s' is not a long integer", value, key));
      }
    }
    throw new IllegalArgumentException(
        String.format("value '%s' for key '%s' is not a long integer", value, key));
  }

  /**
   * Gets a string from an object for the given key.  If the key is not present, this returns null.
   * If the value is not a String, throws an exception.

View on GitHub (pinned to 64daddc1f3)

Solutions

  1. Make the config value an exact whole number (1000.5 -> 1000) or compute nanos with integer arithmetic upstream.
  2. If the value exceeds long range, reduce the unit (use millis/micros) or change the field semantics.
  3. Catch ClassCastException around the call and log the offending key for config debugging.

Example fix

// before
{"keepAliveTimeNanos": 1000.5}
// after
{"keepAliveTimeNanos": 1000}
Defensive patterns

Strategy: validation

Validate before calling

Object v = config.get("keepAliveTimeNanos");
if (v instanceof Double && ((Double) v).longValue() != (Double) v) {
  throw new IllegalArgumentException("keepAliveTimeNanos must be a whole long");
}

Type guard

boolean isWholeLong(Object v) {
  return v instanceof Double && ((Double) v).longValue() == (Double) v;
}

Try / catch

try {
  long l = JsonUtil.getNumberAsLong(config, "keepAliveTimeNanos");
} catch (ClassCastException e) {
  log.error("Long config field has fractional value: " + e.getMessage());
}

Prevention

When it happens

Trigger: JsonUtil.getNumberAsLong(map, key) where map.get(key) is a Double like 2.75, or a huge/precise double that no longer equals its longValue() (e.g. 1e19).

Common situations: Config values such as "keepAliveTimeNanos": 1000.5 from time-unit conversions done in floating point; oversized nanosecond values that exceed long precision when represented as doubles.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/991abfe38daa3ba8. Report an issue: GitHub.