elastic/elasticsearch · error · ScriptException

{ex.getMessage()}

Error message

{ex.getMessage()}

What it means

Thrown by MustacheScriptEngine.compile() when a MustacheException is caught during template compilation. The original MustacheException message is preserved and wrapped in a ScriptException with the template source and engine name ('mustache'). This is the top-level compilation error handler — any syntax error, unsupported feature, or parsing failure in the mustache template surfaces here.

Solutions

  1. Read the inner exception message to identify the specific compilation error (partial unsupported, function parsing, syntax error, etc.)
  2. Validate mustache syntax: ensure all {{ }} tags are properly closed, sections {{#x}}...{{/x}} are balanced
  3. Remove unsupported features: partials ({{>}}), dynamic partials ({{$}}), and any mustache extensions not listed in Elasticsearch docs
  4. Test the template with a standalone mustache validator before sending to Elasticsearch

Example fix

// before — unclosed section tag:
{"query": {"bool": {"must": {{#filters}}{"term": {"x": "{{.}}"}}{{/filters}} ]}}

// after — properly balanced and valid:
{"query": {"bool": {"must": [{{#filters}}{"term": {"x": "{{.}}"}}{{/filters}}]}}}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate template syntax before sending to Elasticsearch
// Use a local mustache parser to check syntax, or use a regex-based pre-check
// for common errors: unclosed tags, unbalanced sections
boolean isBalanced(String src) {
    int depth = 0;
    Pattern p = Pattern.compile("\\{\{(#|/)([^}]+)\}\}");
    Matcher m = p.matcher(src);
    while (m.find()) {
        depth += m.group(1).equals("#") ? 1 : -1;
    }
    return depth == 0;
}

Try / catch

// Catch ScriptException when compiling templates
try {
    engine.compile(templateName, templateSource, context, options);
} catch (ScriptException e) {
    // e.getMessage() contains the original mustache error
    throw new IllegalArgumentException("Template compilation failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Compiling a search template that contains mustache syntax errors or uses unsupported features. The underlying MustacheException could be from partial/dynamic-partial usage (errors 1246/1247), function parsing failures (1248), or any other mustache parser error. The compile() method catches all MustacheException instances and converts them to ScriptException for consistent error reporting.

Common situations: Syntax errors like unclosed tags {{var without }}, mismatched section blocks {{#x}} without {{/x}}, using unsupported partial syntax, or malformed function calls. The actual root cause is in the wrapped exception — read ex.getMessage() to identify the specific compilation failure.

Related errors


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

Appendix: source

Thrown at modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/MustacheScriptEngine.java:90

     * Compile a template string to (in this case) a Mustache object than can
     * later be re-used for execution to fill in missing parameter values.
     *
     * @param templateSource a string representing the template to compile.
     * @return a compiled template object for later execution.
     * */
    @Override
    public <T> T compile(String templateName, String templateSource, ScriptContext<T> context, Map<String, String> options) {
        if (context.instanceClazz.equals(TemplateScript.class) == false) {
            throw new IllegalArgumentException("mustache engine does not know how to handle context [" + context.name + "]");
        }
        final MustacheFactory factory = createMustacheFactory(options);
        Reader reader = new StringReader(templateSource);
        try {
            Mustache template = factory.compile(reader, "query-template");
            TemplateScript.Factory compiled = params -> new MustacheExecutableScript(template, params);
            return context.factoryClazz.cast(compiled);
        } catch (MustacheException ex) {
            throw new ScriptException(ex.getMessage(), ex, List.of(), templateSource, NAME);
        }

    }

    @Override
    public Set<ScriptContext<?>> getSupportedContexts() {
        return Set.of(TemplateScript.CONTEXT, TemplateScript.INGEST_CONTEXT);
    }

    private static CustomMustacheFactory createMustacheFactory(Map<String, String> options) {
        CustomMustacheFactory.Builder builder = CustomMustacheFactory.builder();
        if (options == null || options.isEmpty()) {
            return builder.build();
        }

        if (options.containsKey(Script.CONTENT_TYPE_OPTION)) {
            builder.mediaType(options.get(Script.CONTENT_TYPE_OPTION));
        }

View on GitHub (pinned to db6a809a66)