oracle/graal · error · IllegalArgumentException
Delimiter '%s' is repeated contiguously in "%s"
Error message
Delimiter '%s' is repeated contiguously in "%s"
What it means
Thrown by OptionsParser.splitOptions when splitting a delimited option list and an empty element is produced, which means the delimiter character appears twice in a row. splitOptions uses whitespace as delimiter unless the string starts with a non-letter character, in which case that first character becomes the delimiter (a convenience for comma-separated lists). Contiguous delimiters (e.g. 'a,,b') or a trailing delimiter yield an empty token and this IllegalArgumentException.
Source
Thrown at compiler/src/jdk.graal.compiler.options/src/jdk/graal/compiler/options/OptionsParser.java:161
* whitespace is the delimiter.
*
* @param options string containing a separated list of option settings.
* @return an array of strings containing the individual parsed options.
* @throws IllegalArgumentException if a non-whitespace delimiter is used and the delimiter
* appears repeated contiguously in {@code options}.
*/
public static String[] splitOptions(String options) {
String sepRegex = "\\s+";
String toParse = options;
if (!options.isEmpty() && !Character.isLetter(options.charAt(0))) {
sepRegex = Pattern.quote(options.substring(0, 1));
toParse = options.substring(1);
}
String[] settings = toParse.split(sepRegex);
for (String optionSetting : settings) {
if (optionSetting.isEmpty()) {
throw new IllegalArgumentException(String.format("Delimiter '%s' is repeated contiguously in \"%s\"", options.charAt(0), options));
}
}
return settings;
}
/**
* Looks up an {@link OptionDescriptor} based on a given name.
*
* @param loader source of the available {@link OptionDescriptors}
* @param name the name of the option to look up
* @return the {@link OptionDescriptor} whose name equals {@code name} or null if not such
* descriptor is available
*/
private static OptionDescriptor lookup(Iterable<OptionDescriptors> loader, String name) {
for (OptionDescriptors optionDescriptors : loader) {
OptionDescriptor desc = optionDescriptors.get(name);
if (desc != null) {
return desc;View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remove the doubled/trailing delimiter shown in the message (the full offending string is printed).
- If building the list in code, filter out blank entries before joining: stream().filter(s -> !s.isBlank()).collect(joining(",")).
- If the leading character is intentionally a delimiter, ensure the remainder never contains that character adjacently.
- Validate the string with a regex such as ^(?!.*,,) before passing it in.
Example fix
// before String opts = ",PrintGraph=false,,Dump=none"; String[] settings = OptionsParser.splitOptions(opts); // after String opts = ",PrintGraph=false,Dump=none"; String[] settings = OptionsParser.splitOptions(opts);
Defensive patterns
Strategy: validation
Validate before calling
boolean hasContiguousDelimiters(String options) {
if (options.isEmpty() || Character.isLetter(options.charAt(0))) {
return options.split("\\s+").length != options.trim().split("\\s+").length;
}
String d = options.substring(0, 1);
String rest = options.substring(1);
return rest.isEmpty() || rest.contains(d + d) || rest.endsWith(d);
}
if (hasContiguousDelimiters(opts)) throw new ConfigException("Bad option list: " + opts);
OptionsParser.splitOptions(opts); Try / catch
try {
String[] settings = OptionsParser.splitOptions(options);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("repeated contiguously")) {
settings = Arrays.stream(options.substring(1).split(Pattern.quote(options.substring(0, 1))))
.filter(s -> !s.isEmpty()).toArray(String[]::new); // or reject explicitly
} else throw e;
} Prevention
- Filter blank entries out before joining option lists.
- Never append a delimiter unconditionally; use String.join or a first-element flag.
- Reject trailing delimiters in config validation at your system boundary.
When it happens
Trigger: Calling splitOptions(",Foo=true,,Bar=false") (double comma), splitOptions("Foo=true,Bar=false,") (trailing comma), or a whitespace-delimited string with a doubled separator producing an empty segment. The check is a loop over the split() result asserting no element isEmpty().
Common situations: Building comma-separated option strings via String.join or StringBuilder and leaving an empty entry; user-supplied config where ',,' is typed; programmatically appending ',' between optional segments where one segment is blank; copy-paste from docs leaving a stray comma.
Related errors
- invalid double value: "%s"
- no choice named '%s'
- LIRInstructionVerifierPath is not supported in native image
- no value provided
- Option setting has does not match the pattern <name>=<value>
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/26bbeea7cc28d953.
Report an issue: GitHub.