{"record":{"id":"6931b883b1142473","repo":"elastic/elasticsearch","slug":"missing-composable-template-resource","errorCode":null,"errorMessage":"missing composable template resource [{}]","messagePattern":"missing composable template resource \\[(.+?)\\]","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"critical","filePath":"modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java","lineNumber":270,"sourceCode":"            );\n        } catch (IOException e) {\n            throw new UncheckedIOException(e);\n        }\n    }\n\n    /**\n     * Loads a composable template from {@code org/elasticsearch/kibana/} resources with {@code ${variable}} substitution\n     * using the same semantics as {@code org.elasticsearch.xpack.core.template.TemplateUtils}.\n     * <p>\n     * Templates live in this module (not {@code x-pack} template-resources) so {@code org.elasticsearch.kibana} does not\n     * {@code require org.elasticsearch.xcore}. That {@code requires} breaks JPMS plugin layer resolution: the kibana module\n     * resolves in a layer where {@code org.elasticsearch.xcore} is not on the module path (see {@code PluginsLoader}).\n     */\n    private static ComposableIndexTemplate loadDataStreamComposableTemplate(String resourceFileName, Map<String, String> variables)\n        throws IOException {\n        try (InputStream in = KibanaPlugin.class.getResourceAsStream(resourceFileName)) {\n            if (in == null) {\n                throw new IOException(\"missing composable template resource [\" + resourceFileName + \"]\");\n            }\n            String raw = new String(in.readAllBytes(), StandardCharsets.UTF_8);\n            String source = substituteTemplateVariables(raw, variables);\n            try (\n                var parser = JsonXContent.jsonXContent.createParser(\n                    XContentParserConfiguration.EMPTY,\n                    source.getBytes(StandardCharsets.UTF_8)\n                )\n            ) {\n                return ComposableIndexTemplate.parse(parser);\n            }\n        }\n    }\n\n    /** Same substitution semantics as {@code TemplateUtils.replaceVariables}. */\n    private static String substituteTemplateVariables(String input, Map<String, String> variables) {\n        String template = input;\n        for (Map.Entry<String, String> variable : variables.entrySet()) {","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java#L252-L288","documentation":"Thrown as IOException by KibanaPlugin.loadDataStreamComposableTemplate when the named template resource file cannot be found on the plugin's classpath (getResourceAsStream returns null). This is a plugin packaging/build defect, not a user input error: the kibana module ships composable index-template JSON under org/elasticsearch/kibana/ and this fires if a referenced file is absent from the jar.","triggerScenarios":"Plugin startup or feature initialization calls loadDataStreamComposableTemplate(resourceFileName) and the file is missing from the classpath. Happens after a build that failed to include the resource, a rename of the template file without updating the caller, or a corrupt/incomplete plugin jar.","commonSituations":"Custom build of the kibana module that excluded resources; renaming a .json template file but not the constant referencing it; packaging the plugin without src/main/resources; deploying a partial/manually-assembled jar.","solutions":["Verify the resource file exists under modules/kibana/src/main/resources/org/elasticsearch/kibana/ and matches the name passed in.","Rebuild the kibana module cleanly (./gradlew :modules:kibana:assemble) to ensure resources are packaged.","If the filename was renamed, update the caller in KibanaPlugin to reference the new name."],"exampleFix":"// before: file is kibana-data-stream.json but code asks for kibana-template.json\nloadDataStreamComposableTemplate(\"kibana-template.json\", vars)\n\n// after: align the name with the actual resource file\nloadDataStreamComposableTemplate(\"kibana-data-stream.json\", vars)","handlingStrategy":"validation","validationCode":"// In plugin/build code, assert the resource exists at startup\nString res = \"org/elasticsearch/kibana/\" + resourceFileName;\ntry (var in = KibanaPlugin.class.getResourceAsStream(res)) {\n    if (in == null) throw new IllegalStateException(\"Missing template resource: \" + res);\n}","typeGuard":null,"tryCatchPattern":"// Wrap template loading so startup fails fast with a clear message\ntry {\n    loadDataStreamComposableTemplate(name, vars);\n} catch (IOException e) {\n    throw new RuntimeException(\"kibana template resource missing: \" + name, e);\n}","preventionTips":["Keep template filenames and the constants referencing them in sync; rename atomically.","Run a full :modules:kibana:assemble and smoke-test startup after resource changes.","Do not exclude src/main/resources when packaging the plugin jar."],"tags":["elasticsearch","kibana-plugin","packaging","resource","classpath","startup"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}