{"record":{"id":"991abfe38daa3ba8","repo":"grpc/grpc-java","slug":"number-expected-to-be-long","errorCode":null,"errorMessage":"Number expected to be long: ","messagePattern":"Number expected to be long: ","errorType":"validation","errorClass":"ClassCastException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/grpc/internal/JsonUtil.java","lineNumber":170,"sourceCode":"        String.format(\"value '%s' for key '%s' is not an integer\", value, key));\n  }\n\n  /**\n   * Gets a number from an object for the given key, casted to an long.  If the key is not\n   * present, this returns null.  If the value does not represent a long integer, throws an\n   * exception.\n   */\n  public static Long getNumberAsLong(Map<String, ?> obj, String key) {\n    assert key != null;\n    if (!obj.containsKey(key)) {\n      return null;\n    }\n    Object value = obj.get(key);\n    if (value instanceof Double) {\n      Double d = (Double) value;\n      long l = d.longValue();\n      if (l != d) {\n        throw new ClassCastException(\"Number expected to be long: \" + d);\n      }\n      return l;\n    }\n    if (value instanceof String) {\n      try {\n        return Long.parseLong((String) value);\n      } catch (NumberFormatException e) {\n        throw new IllegalArgumentException(\n            String.format(\"value '%s' for key '%s' is not a long integer\", value, key));\n      }\n    }\n    throw new IllegalArgumentException(\n        String.format(\"value '%s' for key '%s' is not a long integer\", value, key));\n  }\n\n  /**\n   * Gets a string from an object for the given key.  If the key is not present, this returns null.\n   * If the value is not a String, throws an exception.","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/core/src/main/java/io/grpc/internal/JsonUtil.java#L152-L188","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Make the config value an exact whole number (1000.5 -> 1000) or compute nanos with integer arithmetic upstream.","If the value exceeds long range, reduce the unit (use millis/micros) or change the field semantics.","Catch ClassCastException around the call and log the offending key for config debugging."],"exampleFix":"// before\n{\"keepAliveTimeNanos\": 1000.5}\n// after\n{\"keepAliveTimeNanos\": 1000}","handlingStrategy":"validation","validationCode":"Object v = config.get(\"keepAliveTimeNanos\");\nif (v instanceof Double && ((Double) v).longValue() != (Double) v) {\n  throw new IllegalArgumentException(\"keepAliveTimeNanos must be a whole long\");\n}","typeGuard":"boolean isWholeLong(Object v) {\n  return v instanceof Double && ((Double) v).longValue() == (Double) v;\n}","tryCatchPattern":"try {\n  long l = JsonUtil.getNumberAsLong(config, \"keepAliveTimeNanos\");\n} catch (ClassCastException e) {\n  log.error(\"Long config field has fractional value: \" + e.getMessage());\n}","preventionTips":["Compute nanosecond values with integer math, not floating point.","Keep values within Long range/precision.","Prefer writing whole numbers without decimal points in JSON."],"tags":["grpc","json","config","numeric"],"backgroundTag":"type-mismatch","analyzedSha":"64daddc1f3d1975670f769f3e97bde8b2ba32d25","analyzedAt":"2026-09-08T06:14:57.704Z","contentChangedAt":"2026-09-08T06:14:57.704Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}