elastic/elasticsearch · error · InvalidUserDataException

${snippet}: Result can't be first in file

Error message

${snippet}: Result can't be first in file

What it means

For a test-response snippet, after confirming previousTest is non-null, handleSnippet checks that previousTest lives in the same file as the response (previousTest.path().equals(snippet.path())). If they differ, the response is effectively the first test-related snippet in the current file with no paired request in-file, which the test runner cannot associate. The message 'Result can't be first in file' captures this cross-file orphan case.

Source

Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/RestTestsFromDocSnippetTask.java:223

            if (BAD_LANGUAGES.contains(snippet.language())) {
                throw new InvalidUserDataException(snippet + ": Use `js` instead of `" + snippet.language() + "`.");
            }
            if (snippet.testSetup()) {
                testSetup(snippet);
                previousTest = snippet;
                return;
            }
            if (snippet.testTearDown()) {
                testTearDown(snippet);
                previousTest = snippet;
                return;
            }
            if (snippet.testResponse() || snippet.language().equals("console-result")) {
                if (previousTest == null) {
                    throw new InvalidUserDataException(snippet + ": No paired previous test");
                }
                if (previousTest.path().equals(snippet.path()) == false) {
                    throw new InvalidUserDataException(snippet + ": Result can't be first in file");
                }
                response(snippet);
                return;
            }
            if (("js".equals(snippet.language())) && snippet.console() != null && snippet.console()) {
                throw new InvalidUserDataException(snippet + ": Use `[source,console]` instead of `// CONSOLE`.");
            }
            if (snippet.test() || snippet.language().equals("console")) {
                test(snippet);
                previousTest = snippet;
            }
            // Must be an unmarked snippet....
        }

        private void test(Snippet test) {
            setupCurrent(test);

            if (test.continued()) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Move the paired console request snippet into the same file as the response, above it.
  2. If the response belongs to a request in another file, remove the orphaned response from this file.

Example fix

// file_b.asciidoc — before: response orphaned in a new file
// TESTRESPONSE
{"hits":{"total":0}}

// after: bring the request into the same file
// CONSOLE
GET /my-index/_search
// TESTRESPONSE
{"hits":{"total":0}}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure response snippets share the same file as their paired request
void checkResponseSameFile(List<SnippetInfo> snippets) {
    String lastRequestFile = null;
    for (SnippetInfo s : snippets) {
        if (s.isRequest) lastRequestFile = s.filePath;
        if (s.isResponse && !s.filePath.equals(lastRequestFile)) {
            throw new IllegalStateException(
                "Response in " + s.filePath + " pairs with request in " + lastRequestFile
                + " — they must be in the same file");
        }
    }
}

Prevention

When it happens

Trigger: A console-result/TESTRESPONSE snippet is the first test snippet encountered in a document file, but a previousTest from a different file exists (so the null check passed but the path check fails).

Common situations: Splitting one asciidoc file into two and leaving the response block in the second file while its request stayed in the first; or copy-pasting a response block into a new file without its request.

Related errors


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