{"record":{"id":"27ff10aaa28182f3","repo":"elastic/elasticsearch","slug":"extra-content-message-cutoutnonl-matchin","errorCode":null,"errorMessage":"Extra content ${message} ('${cutOutNoNl}') matching [${pattern}]: ${content}","messagePattern":"Extra content (.+?) \\('(.+?)'\\) matching \\[(.+?)\\]: (.+?)","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/ParsingUtils.java","lineNumber":26,"sourceCode":" */\n\npackage org.elasticsearch.gradle.internal.doc;\n\nimport org.gradle.api.InvalidUserDataException;\n\nimport java.util.function.BiConsumer;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class ParsingUtils {\n\n    static void extraContent(String message, String content, int offset, String pattern) {\n        StringBuilder cutOut = new StringBuilder();\n        cutOut.append(content.substring(offset - 6, offset));\n        cutOut.append('*');\n        cutOut.append(content.substring(offset, Math.min(offset + 5, content.length())));\n        String cutOutNoNl = cutOut.toString().replace(\"\\n\", \"\\\\n\");\n        throw new InvalidUserDataException(\"Extra content \" + message + \" ('\" + cutOutNoNl + \"') matching [\" + pattern + \"]: \" + content);\n    }\n\n    /**\n     * Repeatedly match the pattern to the string, calling the closure with the\n     * matchers each time there is a match. If there are characters that don't\n     * match then blow up. If the closure takes two parameters then the second\n     * one is \"is this the last match?\".\n     */\n    static void parse(String content, String pattern, BiConsumer<Matcher, Boolean> testHandler) {\n        if (content == null) {\n            return; // Silly null, only real stuff gets to match!\n        }\n        Matcher m = Pattern.compile(pattern).matcher(content);\n        int offset = 0;\n        while (m.find()) {\n            if (m.start() != offset) {\n                extraContent(\"between [$offset] and [${m.start()}]\", content, offset, pattern);\n            }","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/ParsingUtils.java#L8-L44","documentation":"ParsingUtils.parse walks the snippet content with a regex, tracking how far it has consumed. extra_content is called when characters exist between the current offset and the next match (or after the last match) that the pattern did not account for — i.e. the snippet body has stray text that is not a valid CONSOLE request, comment, or startyaml/endyaml block.","triggerScenarios":"A CONSOLE snippet in an asciidoc/mdx doc contains a line that does not start with an HTTP method (GET/PUT/POST/HEAD/OPTIONS/DELETE), is not a comment (#), and is not part of a request body line following a method line — for example a typo'd method like 'GETT /_search' or a stray prose line mixed into the snippet.","commonSituations":"Editing a documentation snippet and accidentally leaving a blank-but-with-whitespace line in a position the grammar doesn't expect, pasting a multi-line JSON body without a preceding method line, or a line that starts with a keyword the badBody regex excludes (like 'POST' misspelled as 'P0ST').","solutions":["Read the 'cutOutNoNl' context in the message: it shows the characters around offset (* marks the boundary) — fix the text at that position.","Ensure every line in the snippet is either an HTTP method + path, a continuation body line, a '#' comment, or inside startyaml/endyaml.","Remove stray whitespace-only or prose lines from the snippet block."],"exampleFix":"// before — stray line breaks the grammar\nGET /_search\n{ \"query\": { \"match_all\": {} } }\nthis is prose\n\n// after — remove the prose line\nGET /_search\n{ \"query\": { \"match_all\": {} } }","handlingStrategy":"validation","validationCode":"// Lint a console snippet body for stray non-conforming lines\nimport java.util.regex.Pattern;\n\nprivate static final Pattern LINE_OK = Pattern.compile(\n    \"^(GET|PUT|POST|HEAD|OPTIONS|DELETE)\\\\s+\\\\S.*$|^#.*$|^startyaml$|^endyaml$|^\\\\{.*$|^\\\\}.*$|^\\\\[.*$|^\\\".*$|^\\\\s.*$|^\");\n\nvoid checkSnippetBody(String contents) {\n    int lineNo = 0;\n    for (String line : contents.split(\"\\\\R\")) {\n        lineNo++;\n        if (!LINE_OK.matcher(line).matches()) {\n            throw new IllegalStateException(\n                \"Line \" + lineNo + \" may break CONSOLE parser: '\" + line + \"'\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Every line in a console snippet must be an HTTP method+path, a body continuation line, a '#' comment, or a startyaml/endyaml delimiter.","Run a docs-snippet lint check in pre-commit hooks before pushing doc changes.","When pasting JSON bodies, ensure they directly follow a method+path line with no intervening prose."],"tags":["docs","parsing","snippet","regex","console"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}