elastic/elasticsearch · error · MustacheException

Mustache function [{}] must contain one and only one identif

Error message

Mustache function [{}] must contain one and only one identifier

What it means

Thrown by CustomCode.extractVariableName() at compile time when a custom mustache function (toJson, join, url) contains zero or more than one inner code element. The function parsing expects exactly one identifier inside the function block, e.g., {{#toJson}}variable_name{{/toJson}}. If codes is null or codes.length != 1, the function cannot determine which variable to resolve.

Source

Thrown at modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java:193

        @Override
        protected void tag(Writer writer, String tag) throws IOException {
            writer.write(tc.startChars());
            writer.write(tag);
            writer.write(code);
            writer.write(tc.endChars());
        }

        protected abstract Function<String, String> createFunction(Object resolved);

        /**
         * At compile time, this function extracts the name of the variable:
         * {{#toJson}}variable_name{{/toJson}}
         */
        protected static String extractVariableName(String fn, Mustache mustache, TemplateContext tc) {
            Code[] codes = mustache.getCodes();
            if (codes == null || codes.length != 1) {
                throw new MustacheException("Mustache function [" + fn + "] must contain one and only one identifier");
            }

            try (StringWriter capture = new StringWriter()) {
                // Variable name is in plain text and has type WriteCode
                if (codes[0] instanceof WriteCode) {
                    codes[0].execute(capture, List.of());
                } else {
                    codes[0].identity(capture);
                }
                return capture.toString();
            } catch (IOException e) {
                throw new MustacheException("Exception while parsing mustache function [" + fn + "] at line " + tc.line(), e);
            }
        }
    }

    /**
     * This function renders {@link Iterable} and {@link Map} as their JSON representation

View on GitHub (pinned to db6a809a66)

Solutions

  1. Put exactly one variable name inside the function block: {{#toJson}}myvar{{/toJson}}
  2. If you need multiple values as JSON, pass them as a single array or map parameter: {{#toJson}}multi_value_param{{/toJson}} where the param is an array
  3. Ensure the block contains only the variable name with no extra whitespace-only or empty codes
  4. Use {{#join}}myvar{{/join}} (single variable that resolves to an Iterable) for delimiter joining

Example fix

// before — two variables inside toJson:
{{#toJson}}{{field1}}{{field2}}{{/toJson}}

// after — single variable (pass combined data as one param):
{{#toJson}}combined{{/toJson}}
// where 'combined' is provided as a Map or List in params
Defensive patterns

Strategy: validation

Validate before calling

// Validate that custom function blocks contain exactly one identifier
// Parse template and check each {{#toJson}}, {{#join}}, {{#url}} block
// has exactly one variable reference inside

Prevention

When it happens

Trigger: Writing a custom function block with no content, empty content, or multiple variables inside. Example: {{#toJson}}{{var1}}{{var2}}{{/toJson}} (two variables) or {{#toJson}}{{/toJson}} (empty). The mustache compiler produces a Code array from the block content, and extractVariableName checks its length at compile time.

Common situations: Putting multiple variables inside a {{#toJson}}...{{/toJson}} block expecting them to be merged into one JSON object. Leaving the function block empty. Nesting sections or conditionals inside a function block. Using complex expressions instead of a bare variable name.

Related errors


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