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

  1. Find the `// TEST` line via the outer SnippetParserException's file:line and move/delete it so it follows an open CONSOLE snippet.
  2. If the CONSOLE snippet was intentionally removed, remove the orphaned `// TEST[...]` line too.
  3. 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

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


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/b1f2cc22572abe18. Report an issue: GitHub.