{"record":{"id":"26bbeea7cc28d953","repo":"oracle/graal","slug":"delimiter-s-is-repeated-contiguously-in-s","errorCode":null,"errorMessage":"Delimiter '%s' is repeated contiguously in \"%s\"","messagePattern":"Delimiter '(.+?)' is repeated contiguously in \"(.+?)\"","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.options/src/jdk/graal/compiler/options/OptionsParser.java","lineNumber":161,"sourceCode":"     * whitespace is the delimiter.\n     *\n     * @param options string containing a separated list of option settings.\n     * @return an array of strings containing the individual parsed options.\n     * @throws IllegalArgumentException if a non-whitespace delimiter is used and the delimiter\n     *             appears repeated contiguously in {@code options}.\n     */\n    public static String[] splitOptions(String options) {\n        String sepRegex = \"\\\\s+\";\n        String toParse = options;\n        if (!options.isEmpty() && !Character.isLetter(options.charAt(0))) {\n            sepRegex = Pattern.quote(options.substring(0, 1));\n            toParse = options.substring(1);\n        }\n\n        String[] settings = toParse.split(sepRegex);\n        for (String optionSetting : settings) {\n            if (optionSetting.isEmpty()) {\n                throw new IllegalArgumentException(String.format(\"Delimiter '%s' is repeated contiguously in \\\"%s\\\"\", options.charAt(0), options));\n            }\n        }\n        return settings;\n    }\n\n    /**\n     * Looks up an {@link OptionDescriptor} based on a given name.\n     *\n     * @param loader source of the available {@link OptionDescriptors}\n     * @param name the name of the option to look up\n     * @return the {@link OptionDescriptor} whose name equals {@code name} or null if not such\n     *         descriptor is available\n     */\n    private static OptionDescriptor lookup(Iterable<OptionDescriptors> loader, String name) {\n        for (OptionDescriptors optionDescriptors : loader) {\n            OptionDescriptor desc = optionDescriptors.get(name);\n            if (desc != null) {\n                return desc;","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.options/src/jdk/graal/compiler/options/OptionsParser.java#L143-L179","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nString opts = \",PrintGraph=false,,Dump=none\";\nString[] settings = OptionsParser.splitOptions(opts);\n\n// after\nString opts = \",PrintGraph=false,Dump=none\";\nString[] settings = OptionsParser.splitOptions(opts);","handlingStrategy":"validation","validationCode":"boolean hasContiguousDelimiters(String options) {\n    if (options.isEmpty() || Character.isLetter(options.charAt(0))) {\n        return options.split(\"\\\\s+\").length != options.trim().split(\"\\\\s+\").length;\n    }\n    String d = options.substring(0, 1);\n    String rest = options.substring(1);\n    return rest.isEmpty() || rest.contains(d + d) || rest.endsWith(d);\n}\n\nif (hasContiguousDelimiters(opts)) throw new ConfigException(\"Bad option list: \" + opts);\nOptionsParser.splitOptions(opts);","typeGuard":null,"tryCatchPattern":"try {\n    String[] settings = OptionsParser.splitOptions(options);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"repeated contiguously\")) {\n        settings = Arrays.stream(options.substring(1).split(Pattern.quote(options.substring(0, 1))))\n                         .filter(s -> !s.isEmpty()).toArray(String[]::new); // or reject explicitly\n    } else throw e;\n}","preventionTips":["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."],"tags":["graalvm","options","configuration","parsing"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}