{"record":{"id":"458c9940e5edc15b","repo":"elastic/elasticsearch","slug":"snippet-must-follow-test-setup-or-be-first","errorCode":null,"errorMessage":"${snippet} must follow test setup or be first","messagePattern":"(.+?) must follow test setup or be first","errorType":"validation","errorClass":"InvalidUserDataException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/RestTestsFromDocSnippetTask.java","lineNumber":315,"sourceCode":"                replaceBlockQuote(response.contents()).lines().forEach(line -> current.println(\"        \" + line));\n            }\n        }\n\n        private void teardown(final Snippet snippet) {\n            // insert a teardown defined outside of the docs\n            for (final String name : snippet.teardown().split(\",\")) {\n                final String teardown = getTeardowns().get().get(name);\n                if (teardown == null) {\n                    throw new InvalidUserDataException(\"Couldn't find named teardown $name for \" + snippet);\n                }\n                current.println(\"# Named teardown \" + name);\n                current.println(teardown);\n            }\n        }\n\n        private void testTearDown(Snippet snippet) {\n            if (previousTest != null && previousTest.testSetup() == false && lastDocsPath.equals(snippet.path())) {\n                throw new InvalidUserDataException(snippet + \" must follow test setup or be first\");\n            }\n            setupCurrent(snippet);\n            current.println(\"---\");\n            current.println(\"teardown:\");\n            body(snippet, true);\n        }\n\n        void emitDo(\n            String method,\n            String pathAndQuery,\n            String body,\n            String catchPart,\n            List<String> warnings,\n            boolean inSetup,\n            boolean skipShardFailures\n        ) {\n            String[] tokenized = pathAndQuery.split(\"\\\\?\");\n            String path = tokenized[0];","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/doc/RestTestsFromDocSnippetTask.java#L297-L333","documentation":"testTearDown fires when a snippet is a TESTSETUP-style teardown (// TEARDOWN block acting as file-level teardown). It checks: if there is a previousTest AND that previousTest was NOT a testSetup AND the snippet is in the same file as the last docs path (lastDocsPath.equals(snippet.path())), it throws. A file-level teardown must either be the first snippet in the file or immediately follow a // TESTSETUP block.","triggerScenarios":"A // TEARDOWN (file-level) snippet appears in the same file after a regular console request (previousTest.testSetup() is false), rather than after a TESTSETUP or at the top.","commonSituations":"Placing a file-level teardown in the middle of a doc after normal request snippets; the teardown is meant to wrap the whole file and must sit alongside setup.","solutions":["Move the // TEARDOWN snippet to the top of the file (before any console requests) or immediately after a // TESTSETUP snippet.","If the teardown is meant to run after a specific test, use the per-snippet // TEARDOWN[name] form on a console snippet instead of the file-level block."],"exampleFix":"// before — file-level teardown after a request\n// CONSOLE\nGET /_search\n// TEARDOWN\nDELETE /idx\n\n// after — teardown follows setup at top of file\n// TESTSETUP\nPOST /idx/_doc\n{ \"a\": 1 }\n// TEARDOWN\nDELETE /idx\n// CONSOLE\nGET /_search","handlingStrategy":"validation","validationCode":"// Ensure file-level // TEARDOWN is first or follows TESTSETUP, within the same file\nvoid checkFileTeardownOrder(List<SnippetInfo> fileSnippets, String filePath) {\n    for (int i = 0; i < fileSnippets.size(); i++) {\n        SnippetInfo s = fileSnippets.get(i);\n        if (s.isFileLevelTeardown && i > 0) {\n            SnippetInfo prev = fileSnippets.get(i - 1);\n            if (!prev.testSetup) {\n                throw new IllegalStateException(\n                    \"File-level TEARDOWN in \" + filePath\n                    + \" must be first or follow TESTSETUP\");\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Place file-level // TEARDOWN at the top of the file or right after // TESTSETUP.","Use per-snippet // TEARDOWN[name] for teardowns tied to a specific test, not the file-level block.","Remember file-level teardown wraps the whole document — it belongs with setup, not after requests."],"tags":["docs","teardown","testsetup","file-level","console"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}