{"record":{"id":"a2685a4e639e0473","repo":"elastic/elasticsearch","slug":"cannot-generate-source-from-string-template","errorCode":null,"errorMessage":"Cannot generate source from String template","messagePattern":"Cannot generate source from String template","errorType":"exception","errorClass":"GradleException","httpStatus":null,"severity":"error","filePath":"build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/StringTemplateTask.java","lineNumber":93,"sourceCode":"                ST st = new ST(Files.readString(spec.inputFile.toPath(), UTF_8), '$', '$');\n                for (var entry : spec.properties.entrySet()) {\n                    if (entry.getValue().isEmpty()) {\n                        st.add(entry.getKey(), null);\n                    } else {\n                        st.add(entry.getKey(), entry.getValue());\n                    }\n                }\n                // ST's default AutoIndentWriter renders newlines using the platform's\n                // line.separator, so on Windows the output is CRLF regardless of the\n                // input template's own (LF) line endings. Normalize to LF explicitly so\n                // generated sources are consistent across OSes and match what spotless\n                // (configured with LineEnding.UNIX) expects.\n                String output = st.render().replace(\"\\r\\n\", \"\\n\");\n                Files.createDirectories(outputRootFolder.toPath().resolve(spec.outputFile).getParent());\n                Files.writeString(new File(outputRootFolder, spec.outputFile).toPath(), output, UTF_8);\n                getLogger().info(\"StringTemplateTask generated {}\", spec.outputFile);\n            } catch (IOException e) {\n                throw new GradleException(\"Cannot generate source from String template\", e);\n            }\n        }\n    }\n\n    class TemplateSpec {\n        private File inputFile;\n\n        private String outputFile;\n\n        private Map<String, String> properties;\n\n        @InputFile\n        @PathSensitive(PathSensitivity.RELATIVE)\n        public File getInputFile() {\n            return inputFile;\n        }\n\n        public void setInputFile(File inputFile) {","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/StringTemplateTask.java#L75-L111","documentation":"Thrown by StringTemplateTask.generate when an IOException occurs while rendering a StringTemplate (ST) template and writing the generated file. The task reads the input template, injects properties via st.add, renders with st.render(), normalizes CRLF to LF, creates parent directories, and writes the output; any I/O failure in that sequence is wrapped in this GradleException. Note ST's own template-syntax errors throw STException (a RuntimeException), not IOException — this catch specifically covers filesystem/encoding I/O.","triggerScenarios":"Inside the per-template try block: Files.readString(spec.inputFile.toPath()) fails (input missing/unreadable), st.render() is fine but Files.createDirectories(...) fails (permission/disk), or Files.writeString(...) fails (output path unwritable, parent is a file not a directory). The catch is narrow: catch (IOException e).","commonSituations":"The configured inputFile does not exist or is not readable; the outputFolder points to a read-only location or a path whose parent is a regular file; disk full or permissions denied during write; the output directory was deleted out from under the task; the input file is locked by another process on Windows.","solutions":["Verify spec.inputFile exists and is readable (check the task's input file configuration and the logged 'StringTemplateTask generating {}' line just before the failure).","Ensure getOutputFolder() resolves to a writable directory and that no regular file occupies a needed parent path.","Check filesystem permissions and free space on the output volume.","If running on Windows and the file is locked, close the holder (IDE/antivirus) or rerun.","Distinguish from ST syntax errors: a template-parse failure surfaces as org.stringtemplate.v4.STException, not this message — fix the template delimiters ($...$) if that is the actual cause."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight before the task runs:\nimport java.nio.file.*;\nPath in = spec.inputFile.toPath();\nif (!Files.isReadable(in)) throw new IllegalArgumentException(\"unreadable template: \" + in);\nPath outDir = outputFolder.getAsFile().get().toPath();\nPath outParent = outDir.resolve(spec.outputFile).getParent();\nif (Files.exists(outParent) && !Files.isDirectory(outParent))\n    throw new IllegalArgumentException(\"output parent is not a directory: \" + outParent);\nif (!Files.isWritable(outParent))\n    throw new IllegalArgumentException(\"output dir not writable: \" + outParent);","typeGuard":null,"tryCatchPattern":"try {\n    st.add(...); st.render(); Files.writeString(...);\n} catch (java.io.IOException e) {\n    throw new GradleException(\"Cannot generate source from String template for \"\n        + spec.inputFile + \" -> \" + spec.outputFile, e);\n}","preventionTips":["Validate the input template path exists and is readable before the task runs.","Ensure getOutputFolder() is a writable directory and no regular file occupies a parent slot.","Distinguish IOException (filesystem) from STException (template syntax); fix $...$ delimiters for the latter."],"tags":["gradle","code-generation","stringtemplate","io","filesystem"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}