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

  1. Remove the doubled/trailing delimiter shown in the message (the full offending string is printed).
  2. If building the list in code, filter out blank entries before joining: stream().filter(s -> !s.isBlank()).collect(joining(",")).
  3. If the leading character is intentionally a delimiter, ensure the remainder never contains that character adjacently.
  4. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/26bbeea7cc28d953. Report an issue: GitHub.