oracle/graal · error · IllegalArgumentException
option ' ' must have value, not [toString: ]
Error message
%s option '%s' must have %s value, not %s [toString: %s]
What it means
Thrown by OptionsParser.parseOptionValue when the value object is not a String and its runtime class does not exactly equal the option's declared type. When values arrive as Strings they go through per-type parsing, but non-String values (used when options are set programmatically via an EconomicMap rather than parsed from text) must already be the exact boxed type (Boolean, Integer, Long, Float, Double, etc.).
Solutions
- Convert the value to the exact declared type shown in the message (e.g. ((Number)v).longValue() for a Long option).
- Prefer passing the value as a String and let parseOptionValue do the typed parsing (String branch handles Boolean/Enum/numbers).
- When loading config generically, coerce numeric types to the option type from desc.getOptionValueType().
- Check the '[toString: ...]' part of the message to confirm what object actually arrived.
Example fix
// before OptionDescriptor d = ...; // option type Long Object v = 42; // Integer Object parsed = OptionsParser.parseOptionValue(d, v); // after Object v = Long.valueOf(42); // exact Long // or simply pass the string form: Object parsed = OptionsParser.parseOptionValue(d, "42");
Defensive patterns
Strategy: type-guard
Validate before calling
Object coerceToOptionType(OptionDescriptor desc, Object v) {
Class<?> t = desc.getOptionValueType();
if (v instanceof String s) return OptionsParser.parseOptionValue(desc, s);
if (t == Long.class && v instanceof Number n) return n.longValue();
if (t == Integer.class && v instanceof Number n) return n.intValue();
if (t == Double.class && v instanceof Number n) return n.doubleValue();
if (t == Float.class && v instanceof Number n) return n.floatValue();
if (t == Boolean.class && v instanceof Boolean) return v;
if (v != null && v.getClass() == t) return v;
throw new IllegalArgumentException("Cannot coerce " + v + " to " + t.getSimpleName());
} Type guard
boolean matchesOptionType(OptionDescriptor desc, Object v) {
return v != null && v.getClass() == desc.getOptionValueType();
} Try / catch
try {
value = OptionsParser.parseOptionValue(desc, uncheckedValue);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("must have") && e.getMessage().contains("[toString:")) {
value = OptionsParser.parseOptionValue(desc, String.valueOf(uncheckedValue)); // re-parse from string form
} else throw e;
} Prevention
- Prefer String values when feeding options programmatically; the parser then converts per-type.
- Coerce generic config numbers (JSON/YAML) to the exact boxed type from getOptionValueType().
- Remember the check is exact class equality: subclasses and Integer-for-Long both fail.
When it happens
Trigger: Calling parseOptionValue(desc, someObject) where someObject is, say, an Integer for a Long-typed option (Integer.class != Long.class), a primitive-converted Short, or an arbitrary object. Note the comparison is exact-class equality, not instanceof, so subclasses and wider/narrower numeric boxes all fail.
Common situations: Programmatically seeding option maps from untyped config (JSON/YAML numbers parsed as Integer when the option is Long); passing an enum's name String where an EnumOptionKey expects the Enum instance is fine (String branch), but passing a boxed Integer where Boolean is expected fails; reflective code pulling values from a Map<Object,Object>.
Related errors
- Option setting has does not match the pattern
- Annotation value type
- Boolean option ' ' must have value "true" or "false", not
- Could not find option
- Delimiter ' ' is repeated contiguously in
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/d8a13ea4e8cde454.
Report an issue: GitHub.
Appendix: source
Thrown at compiler/src/jdk.graal.compiler.options/src/jdk/graal/compiler/options/OptionsParser.java:228
}
throw new IllegalArgumentException(msg.toString());
}
Object value = parseOptionValue(desc, uncheckedValue);
desc.getOptionKey().update(values, value);
}
/**
* Parses a given option value with a known descriptor.
*/
public static Object parseOptionValue(OptionDescriptor desc, Object uncheckedValue) {
Class<?> optionType = desc.getOptionValueType();
Object value;
if (!(uncheckedValue instanceof String valueString)) {
if (optionType != uncheckedValue.getClass()) {
String type = optionType.getSimpleName();
throw new IllegalArgumentException(type + " option '" + desc.getName() + "' must have " + type + " value, not " + uncheckedValue.getClass() + " [toString: " + uncheckedValue + "]");
}
value = uncheckedValue;
} else {
if (optionType == Boolean.class) {
if ("true".equals(valueString)) {
value = Boolean.TRUE;
} else if ("false".equals(valueString)) {
value = Boolean.FALSE;
} else {
throw new IllegalArgumentException("Boolean option '" + desc.getName() + "' must have value \"true\" or \"false\", not \"" + uncheckedValue + "\"");
}
} else if (optionType == String.class) {
value = valueString;
} else if (Enum.class.isAssignableFrom(optionType)) {
value = ((EnumOptionKey<?>) desc.getOptionKey()).valueOf(valueString);
} else if (optionType == EconomicSet.class) {
value = ((EnumMultiOptionKey<?>) desc.getOptionKey()).valueOf(valueString);
} else {View on GitHub (pinned to a66e9ccd1d)