elastic/elasticsearch · critical · IOException

missing composable template resource [{}]

Error message

missing composable template resource [{}]

What it means

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.

Source

Thrown at modules/kibana/src/main/java/org/elasticsearch/kibana/KibanaPlugin.java:270

            );
        } catch (IOException e) {
            throw new UncheckedIOException(e);
        }
    }

    /**
     * Loads a composable template from {@code org/elasticsearch/kibana/} resources with {@code ${variable}} substitution
     * using the same semantics as {@code org.elasticsearch.xpack.core.template.TemplateUtils}.
     * <p>
     * Templates live in this module (not {@code x-pack} template-resources) so {@code org.elasticsearch.kibana} does not
     * {@code require org.elasticsearch.xcore}. That {@code requires} breaks JPMS plugin layer resolution: the kibana module
     * resolves in a layer where {@code org.elasticsearch.xcore} is not on the module path (see {@code PluginsLoader}).
     */
    private static ComposableIndexTemplate loadDataStreamComposableTemplate(String resourceFileName, Map<String, String> variables)
        throws IOException {
        try (InputStream in = KibanaPlugin.class.getResourceAsStream(resourceFileName)) {
            if (in == null) {
                throw new IOException("missing composable template resource [" + resourceFileName + "]");
            }
            String raw = new String(in.readAllBytes(), StandardCharsets.UTF_8);
            String source = substituteTemplateVariables(raw, variables);
            try (
                var parser = JsonXContent.jsonXContent.createParser(
                    XContentParserConfiguration.EMPTY,
                    source.getBytes(StandardCharsets.UTF_8)
                )
            ) {
                return ComposableIndexTemplate.parse(parser);
            }
        }
    }

    /** Same substitution semantics as {@code TemplateUtils.replaceVariables}. */
    private static String substituteTemplateVariables(String input, Map<String, String> variables) {
        String template = input;
        for (Map.Entry<String, String> variable : variables.entrySet()) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Verify the resource file exists under modules/kibana/src/main/resources/org/elasticsearch/kibana/ and matches the name passed in.
  2. Rebuild the kibana module cleanly (./gradlew :modules:kibana:assemble) to ensure resources are packaged.
  3. If the filename was renamed, update the caller in KibanaPlugin to reference the new name.

Example fix

// before: file is kibana-data-stream.json but code asks for kibana-template.json
loadDataStreamComposableTemplate("kibana-template.json", vars)

// after: align the name with the actual resource file
loadDataStreamComposableTemplate("kibana-data-stream.json", vars)
Defensive patterns

Strategy: validation

Validate before calling

// In plugin/build code, assert the resource exists at startup
String res = "org/elasticsearch/kibana/" + resourceFileName;
try (var in = KibanaPlugin.class.getResourceAsStream(res)) {
    if (in == null) throw new IllegalStateException("Missing template resource: " + res);
}

Try / catch

// Wrap template loading so startup fails fast with a clear message
try {
    loadDataStreamComposableTemplate(name, vars);
} catch (IOException e) {
    throw new RuntimeException("kibana template resource missing: " + name, e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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