{"record":{"id":"cc54e5ebf9874424","repo":"grpc/grpc-java","slug":"number-expected-to-be-integer","errorCode":null,"errorMessage":"Number expected to be integer: ","messagePattern":"Number expected to be integer: ","errorType":"validation","errorClass":"ClassCastException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/grpc/internal/JsonUtil.java","lineNumber":139,"sourceCode":"        String.format(\"value '%s' for key '%s' in '%s' is not a number\", value, key, obj));\n  }\n\n  /**\n   * Gets a number from an object for the given key, casted to an integer.  If the key is not\n   * present, this returns null.  If the value does not represent an integer, throws an exception.\n   */\n  @Nullable\n  public static Integer getNumberAsInteger(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      int i = d.intValue();\n      if (i != d) {\n        throw new ClassCastException(\"Number expected to be integer: \" + d);\n      }\n      return i;\n    }\n    if (value instanceof String) {\n      try {\n        return Integer.parseInt((String) value);\n      } catch (NumberFormatException e) {\n        throw new IllegalArgumentException(\n            String.format(\"value '%s' for key '%s' is not an integer\", value, key));\n      }\n    }\n    throw new IllegalArgumentException(\n        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","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/grpc/grpc-java/blob/64daddc1f3d1975670f769f3e97bde8b2ba32d25/core/src/main/java/io/grpc/internal/JsonUtil.java#L121-L157","documentation":"getNumberAsInteger reads a numeric value from a parsed JSON config map and casts it to int. If the stored value is a Double with a fractional part (e.g. 1.5), the int cast would silently truncate, so gRPC throws a ClassCastException instead. It exists to catch lossy numeric conversions in service config parsing.","triggerScenarios":"Calling JsonUtil.getNumberAsInteger(map, key) where map.get(key) is a Double whose intValue() differs from the original value, e.g. JSON {\"maxAttempts\": 1.5} or 1e10 (outside int range).","commonSituations":"Hand-edited service config or channel argument JSON where someone wrote a decimal like 3.0 is fine but 3.5, or a value exceeding Integer.MAX_VALUE such as 5000000000; configs generated by tools that emit all numbers as doubles.","solutions":["Change the JSON/config value to a whole number that fits in an int (e.g. 1.5 -> 2, 5000000000 -> a value <= 2147483647).","If the field is genuinely large, switch to a long-based accessor such as getNumberAsLong and widen the consuming field.","Wrap the call in try/catch for ClassCastException and fail fast with a clear config-validation message at startup."],"exampleFix":"// before\n{\"timeoutMillis\": 4294967296}\n// after\n{\"timeoutMillis\": 2147483647}","handlingStrategy":"validation","validationCode":"Object v = config.get(\"maxAttempts\");\nif (v instanceof Double && ((Double) v).intValue() != (Double) v) {\n  throw new IllegalArgumentException(\"maxAttempts must be a whole int\");\n}","typeGuard":"boolean isWholeInt(Object v) {\n  return v instanceof Double && ((Double) v).intValue() == (Double) v;\n}","tryCatchPattern":"try {\n  int n = JsonUtil.getNumberAsInteger(config, \"maxAttempts\");\n} catch (ClassCastException e) {\n  log.error(\"Bad integer config: \" + e.getMessage());\n  throw new ConfigException(e);\n}","preventionTips":["Never emit fractional numbers for int fields in generated configs.","Keep int-range values <= 2147483647.","Run JSON Schema validation over service config before parsing."],"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"}