elastic/elasticsearch · error · InvalidUserDataException
TEST not paired with a snippet at
Error message
TEST not paired with a snippet at
What it means
Thrown by SnippetParser.testHandled() when a line matching the TEST regex is found but snippetBuilder is null — i.e. a `// TEST[...]` directive appears with no currently-open CONSOLE snippet to attach to. TEST directives express substitutions, catches, setup/teardown, and warnings against the preceding snippet; without one they are meaningless. Like error 209 the literal message ends with 'at ' and the location is supplied by the outer SnippetParserException wrapper.
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/SnippetParser.java:179
snippetBuilder.withSubstitution("( +)", "$1\\\\s+");
snippetBuilder.withSubstitution("\n", "\\\\s*\n ");
} else if (m.group(4) != null) {
// TESTRESPONSE[skip:reason]
snippetBuilder.withSkip(m.group(4));
}
}
);
}
return true;
}
return false;
}
protected boolean testHandled(String line, SnippetBuilder snippetBuilder) {
Matcher matcher = testPattern().matcher(line);
if (matcher.matches()) {
if (snippetBuilder == null) {
throw new InvalidUserDataException("TEST not paired with a snippet at ");
}
snippetBuilder.withTest(true);
if (matcher.group(2) != null) {
ParsingUtils.parse(matcher.group(2), TEST_SYNTAX, (Matcher m, Boolean last) -> {
if (m.group(1) != null) {
snippetBuilder.withCatchPart(m.group(1));
return;
}
if (m.group(2) != null) {
snippetBuilder.withSubstitution(m.group(2), m.group(3));
return;
}
if (m.group(4) != null) {
snippetBuilder.withSkip(m.group(4));
return;
}
if (m.group(5) != null) {
snippetBuilder.withContinued(true);View on GitHub (pinned to db6a809a66)
Solutions
- Find the `// TEST` line via the outer SnippetParserException's file:line and move/delete it so it follows an open CONSOLE snippet.
- If the CONSOLE snippet was intentionally removed, remove the orphaned `// TEST[...]` line too.
- Check ordering: TEST directives must come after the snippet they modify, not before.
Example fix
// before: // TEST[s/old/new/] // CONSOLE GET /old // after: // CONSOLE GET /old // TEST[s/old/new/]
Defensive patterns
Strategy: validation
Validate before calling
// Before building, walk doc lines and ensure every // TEST[...] has a preceding open // CONSOLE snippet.
Prevention
- Place `// TEST[...]` directives after the snippet they modify.
- Delete `// TEST[...]` lines when you delete their snippet.
- Don't reorder snippets without reordering their TEST directives.
When it happens
Trigger: A `// TEST[...]` line is not preceded by an open `// CONSOLE` snippet block (snippet was closed, deleted, or never opened). The testPattern matches and snippetBuilder is null.
Common situations: A TEST directive was left behind after its CONSOLE snippet was deleted; the TEST directive was placed before (instead of after) the snippet; a copy-paste duplicated the TEST directive away from its snippet.
Related errors
- Unexpected unconverted snippets: {foundButNotListed}
- Couldn't find the corresponding mdx file for {asciidocFileAb
- Yaml rest specs ({asciidocFile} and {mdxFile}) are not equal
- Yaml rest specs ({asciidocFile} and {mdxFile}) are not equal
- {name}: Snippet missing a language. This is required by Elas
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/b1f2cc22572abe18.
Report an issue: GitHub.