{"record":{"id":"b8de672e73e920d3","repo":"elastic/elasticsearch","slug":"test-continued-cannot-immediately-follow-te-b8de67","errorCode":null,"errorMessage":"// TEST[continued] cannot immediately follow // TEARDOWN: ${test}","messagePattern":"// TEST\\[continued\\] cannot immediately follow // TEARDOWN: (.+?)","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/RestTestsFromDocSnippetTask.java","lineNumber":251,"sourceCode":"                previousTest = snippet;\n            }\n            // Must be an unmarked snippet....\n        }\n\n        private void test(Snippet test) {\n            setupCurrent(test);\n\n            if (test.continued()) {\n                /* Catch some difficult to debug errors with // TEST[continued]\n                 * and throw a helpful error message. */\n                if (previousTest == null || previousTest.path().equals(test.path()) == false) {\n                    throw new InvalidUserDataException(\"// TEST[continued] \" + \"cannot be on first snippet in a file: \" + test);\n                }\n                if (previousTest != null && previousTest.testSetup()) {\n                    throw new InvalidUserDataException(\"// TEST[continued] \" + \"cannot immediately follow // TESTSETUP: \" + test);\n                }\n                if (previousTest != null && previousTest.testSetup()) {\n                    throw new InvalidUserDataException(\"// TEST[continued] \" + \"cannot immediately follow // TEARDOWN: \" + test);\n                }\n            } else {\n                current.println(\"---\");\n                if (test.name() != null && test.name().isBlank() == false) {\n                    if (names.add(test.name()) == false) {\n                        throw new InvalidUserDataException(\"Duplicated snippet name '\" + test.name() + \"': \" + test);\n                    }\n                    current.println(\"\\\"\" + test.name() + \"\\\":\");\n                } else {\n                    current.println(\"\\\"line_\" + test.start() + \"\\\":\");\n                }\n                /* The Elasticsearch test runner doesn't support quite a few\n                 * constructs unless we output this skip. We don't know if\n                 * we're going to use these constructs, but we might so we\n                 * output the skip just in case. */\n                current.println(\"  - skip:\");\n                current.println(\"      features:\");\n                current.println(\"        - default_shards\");","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/RestTestsFromDocSnippetTask.java#L233-L269","documentation":"This is the third guard inside test.continued(). The source at lines 250-251 checks 'previousTest != null && previousTest.testSetup()' but the message says 'cannot immediately follow // TEARDOWN'. This is a suspected copy-paste defect: the condition likely should call previousTest.testTearDown() to detect a continued snippet following a teardown. As written, it fires under the same condition as the TESTSETUP message (previousTest is a setup), so the TEARDOWN label is misleading. A continued snippet is invalid after a teardown because teardowns close out a test section.","triggerScenarios":"Per current code: identical trigger to the TESTSETUP message (previousTest is a setup). Per intent: a // TEST[continued] snippet immediately follows a // TEARDOWN snippet in the same file.","commonSituations":"Reordering snippets so a teardown is followed by a continued test; the continued block has no open test section to append to after a teardown closed it.","solutions":["Add a new base console request snippet between the teardown and the // TEST[continued] snippet.","Remove // TEST[continued] if the snippet should stand alone as a new test.","If you maintain build-tools-internal, consider fixing the condition at line 250 to call testTearDown() so the message matches its true trigger."],"exampleFix":"// before — continued after teardown\n// TEARDOWN\nDELETE /idx\n// CONSOLE\n// TEST[continued]\nGET /idx/_search\n\n// after — new base request first\n// TEARDOWN\nDELETE /idx\n// CONSOLE\nPOST /idx2/_doc\n{ \"a\": 1 }\n// CONSOLE\n// TEST[continued]\nGET /idx2/_search","handlingStrategy":"validation","validationCode":"// Ensure TEST[continued] does not immediately follow a TEARDOWN (intent of the guard)\n// NOTE: source code at line 250 checks testSetup() rather than testTearDown();\n// this lint checks the intended condition.\nvoid checkContinuedAfterTeardown(List<SnippetInfo> fileSnippets, String filePath) {\n    for (int i = 1; i < fileSnippets.size(); i++) {\n        SnippetInfo prev = fileSnippets.get(i - 1);\n        SnippetInfo cur = fileSnippets.get(i);\n        if (cur.continued && prev.testTearDown) {\n            throw new IllegalStateException(\n                \"// TEST[continued] in \" + filePath + \" immediately follows TEARDOWN\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never place // TEST[continued] directly after a teardown snippet; insert a new base request first.","If you maintain build-tools-internal, consider fixing line 250 to call testTearDown() so the message matches its trigger.","Treat teardown as closing a test section — nothing can continue from it."],"tags":["docs","snippet","test-continued","teardown","bug-suspected","console"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}