elastic/elasticsearch · error · InvalidUserDataException

Continued snippets can't be skipped

Error message

Continued snippets can't be skipped

What it means

In TestBuilder.test, if test.skip() is non-null AND test.continued() is true, the throw fires. A continued snippet appends its steps to the previous test's YAML, so it cannot carry its own 'always_skip' directive — the skip semantics belong to the whole test, not a continuation fragment.

Source

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

                    current.println("\"" + test.name() + "\":");
                } else {
                    current.println("\"line_" + test.start() + "\":");
                }
                /* The Elasticsearch test runner doesn't support quite a few
                 * constructs unless we output this skip. We don't know if
                 * we're going to use these constructs, but we might so we
                 * output the skip just in case. */
                current.println("  - skip:");
                current.println("      features:");
                current.println("        - default_shards");
                current.println("        - stash_in_key");
                current.println("        - stash_in_path");
                current.println("        - stash_path_replace");
                current.println("        - warnings");
            }
            if (test.skip() != null) {
                if (test.continued()) {
                    throw new InvalidUserDataException("Continued snippets " + "can't be skipped");
                }
                current.println("        - always_skip");
                current.println("      reason: " + test.skip());
            }
            if (test.setup() != null) {
                setup(test);
            }

            body(test, false);

            if (test.teardown() != null) {
                teardown(test);
            }
        }

        private void response(Snippet response) {
            if (null == response.skip()) {
                current.println("  - match:");

View on GitHub (pinned to db6a809a66)

Solutions

  1. Move the skip directive onto the base (first, non-continued) test snippet in the chain instead of the continued fragment.
  2. Remove // TEST[continued] if this snippet must be skipped on its own (making it a standalone test).

Example fix

// before — skip on a continued fragment
// CONSOLE
POST /idx/_doc
{ "a": 1 }
// CONSOLE
// TEST[continued,skip:feature-x]
GET /idx/_search

// after — skip on the base test
// CONSOLE
// TEST[skip:feature-x]
POST /idx/_doc
{ "a": 1 }
// CONSOLE
// TEST[continued]
GET /idx/_search
Defensive patterns

Strategy: validation

Validate before calling

// Reject snippets that combine 'continued' and 'skip'
void checkContinuedNotSkipped(SnippetInfo s, String filePath) {
    if (s.continued && s.skip != null) {
        throw new IllegalStateException(
            "Snippet " + s.path + " in " + filePath
            + " is both continued and skipped — move skip to the base test");
    }
}

Type guard

boolean continuedAndSkipCompatible(boolean continued, String skip) {
    return !(continued && skip != null);
}

Prevention

When it happens

Trigger: A single snippet is annotated with both // TEST[continued] and a skip directive (e.g. // TEST[continued,skip:reason]).

Common situations: Trying to skip a continuation fragment independently of its base test; combining flags without realizing they conflict.

Related errors


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