ben-manes/caffeine · error · IllegalArgumentException

key %s value was set to %s, must be an integer

Error message

key %s value was set to %s, must be an integer

What it means

CaffeineSpec.parseInt wraps Integer.parseInt and throws IllegalArgumentException("key %s value was set to %s, must be an integer") when an option that requires an int (e.g. initialCapacity) receives a non-integer value. The message names both the offending key and the raw value so the bad setting can be located directly in the spec string.

Source

Thrown at caffeine/src/main/java/com/github/benmanes/caffeine/cache/CaffeineSpec.java:279

    requireArgument(refreshAfterWrite == null, "refreshAfterWrite was already set");
    refreshAfterWrite = parseDuration(key, value);
  }

  /** Configures the value as weak or soft references. */
  void recordStats(@Nullable String value) {
    requireArgument(value == null, "record stats does not take a value");
    requireArgument(!recordStats, "record stats was already set");
    recordStats = true;
  }

  /** Returns a parsed int value. */
  static int parseInt(String key, @Nullable String value) {
    requireArgument((value != null) && !value.isEmpty(), "value of key %s was omitted", key);
    requireNonNull(value);
    try {
      return Integer.parseInt(normalizeNumericLiteral(value));
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException(String.format(US,
          "key %s value was set to %s, must be an integer", key, value), e);
    }
  }

  /** Returns a parsed long value. */
  static long parseLong(String key, @Nullable String value) {
    requireArgument((value != null) && !value.isEmpty(), "value of key %s was omitted", key);
    requireNonNull(value);
    try {
      return Long.parseLong(normalizeNumericLiteral(value));
    } catch (NumberFormatException e) {
      throw new IllegalArgumentException(String.format(US,
          "key %s value was set to %s, must be a long", key, value), e);
    }
  }

  /** Returns the value after adjusting for underscores in a numeric literal. */
  static String normalizeNumericLiteral(String value) {

View on GitHub (pinned to 9da6581ee3)

Solutions

  1. Use a plain decimal integer for the key shown in the message, e.g. maximumSize=1000 (commas are not valid)
  2. Underscores as thousands separators are allowed only between digits: 1_000_000 is fine, _1000 and 1_000_ are not
  3. Strip formatting before injecting values into the spec string

Example fix

# before
Caffeine.from("initialCapacity=1,024") // IllegalArgumentException: must be an integer

# after
Caffeine.from("initialCapacity=1024")
Defensive patterns

Strategy: validation

Validate before calling

// Validate int options before building the spec:
static String intOption(String key, String rawValue) {
  String normalized = rawValue.replace("_", "").replace(",", "").strip();
  if (!normalized.matches("[+-]?\\d+")) {
    throw new ConfigException(key + " must be an integer, got: " + rawValue);
  }
  return key + "=" + normalized;
}
String spec = intOption("initialCapacity", envValue);

Try / catch

try {
  return Caffeine.from(spec);
} catch (IllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("must be an integer")) {
    log.error("Non-integer value in cache spec: {}", spec, e);
    return Caffeine.newBuilder(); // fall back to defaults
  }
  throw e;
}

Prevention

When it happens

Trigger: Caffeine.from("initialCapacity=1_0x"), values with commas ("maximumSize=1,000"), decimal values ("initialCapacity=64.5"), or embedded units ("initialCapacity=64k") on an int option.

Common situations: Human-formatted numbers (thousands separators) pasted from docs into config; locale-formatted values; environment variable interpolation inserting stray characters; underscores misplaced (leading/trailing, after sign) which normalizeNumericLiteral leaves as-is.

Related errors


AI-assisted analysis of ben-manes/caffeine@9da6581ee3 (2026-08-14). Data as JSON: /api/errors/bca2f8ee1f226b7c. Report an issue: GitHub.