elastic/elasticsearch · error · IOException

Template property '{}' not found in template: {}

Error message

Template property '{}' not found in template:
{}

What it means

Thrown in tweakJvmOptions when a ReplacementKey's expected placeholder is not found in the distro's jvm.options file, after trying both the primary key and the fallback. The plugin rewrites specific jvm.options lines (heap, ErrorFile, GC logs) by string replacement; if the template changed shape, the replacement cannot proceed safely.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:1502

        }

        tweakJvmOptions(configFileRoot);
        LOGGER.info("Written config file:{} for {}", configFile, this);
    }

    private void tweakJvmOptions(Path configFileRoot) {
        LOGGER.info("Tweak jvm options {}.", configFileRoot.resolve("jvm.options"));
        Path jvmOptions = configFileRoot.resolve("jvm.options");
        try {
            String content = new String(Files.readAllBytes(jvmOptions));
            Map<ReplacementKey, String> expansions = jvmOptionExpansions();
            for (var entry : expansions.entrySet()) {
                ReplacementKey replacement = entry.getKey();
                String key = replacement.key();
                if (content.contains(key) == false) {
                    key = replacement.fallback();
                    if (content.contains(key) == false) {
                        throw new IOException("Template property '" + replacement + "' not found in template:\n" + content);
                    }
                }
                content = content.replace(key, entry.getValue());
            }
            Files.write(jvmOptions, content.getBytes());
        } catch (IOException ioException) {
            throw new UncheckedIOException(ioException);
        }
    }

    private record ReplacementKey(String key, String fallback) {}

    private Map<ReplacementKey, String> jvmOptionExpansions() {
        Map<ReplacementKey, String> expansions = new HashMap<>();
        Version version = getVersion();

        ReplacementKey heapDumpPathSub;
        if (version.before("8.19.0") && version.onOrAfter("6.3.0")) {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Open the distro's jvm.options file (getDistroDir()/config/jvm.options) and find the line that should contain the placeholder.
  2. Update the ReplacementKey in jvmOptionExpansions() to match the actual current template string.
  3. If the placeholder genuinely moved, add a new fallback key covering the older format for backport compatibility.
  4. Re-extract the distro in case it is stale relative to the current source.

Example fix

// before: ES changed ErrorFile path format
errorFileSub = new ReplacementKey("-XX:ErrorFile=logs/hs_err_pid%p.log", null);
// after: cover both old and new formats
errorFileSub = new ReplacementKey("-XX:ErrorFile=logs/hs_err_pid%p.log", "-XX:ErrorFile=hs_err_pid%p.log");
Defensive patterns

Strategy: validation

Validate before calling

String tmpl = Files.readString(jvmOptions);
for (var e : jvmOptionExpansions().entrySet()) {
    ReplacementKey rk = e.getKey();
    if (!tmpl.contains(rk.key()) && (rk.fallback() == null || !tmpl.contains(rk.fallback()))) {
        throw new IllegalStateException("jvm.options template missing placeholder for " + rk);
    }
}

Prevention

When it happens

Trigger: jvmOptionExpansions() returns a ReplacementKey whose key() and fallback() strings are both absent from the read jvm.options content. Happens when the ES distribution version changed the jvm.options format (renamed a flag, changed a path) and the build-tools' expected template has not been updated to match.

Common situations: Bumping the ES version under test without updating build-tools; backporting changes where the old jvm.options format differs; a custom distribution ships a non-standard jvm.options.

Related errors


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