elastic/elasticsearch · error · ParsingException

Could not parse inline template

Error message

Could not parse inline template

What it means

Raised while parsing the inline template ('source' field) when it is provided as a JSON object (START_OBJECT) and copying that structure into a JSON XContentBuilder throws an IOException. The template engine only supports JSON-encoded inline templates, so any I/O or structural failure during the copy is wrapped as a ParsingException located at the parser's current token.

Source

Thrown at modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/SearchTemplateRequest.java:217

    private static final ObjectParser<SearchTemplateRequest, Void> PARSER;

    static {
        PARSER = new ObjectParser<>("search_template");
        PARSER.declareField((parser, request, s) -> request.setScriptParams(parser.map()), PARAMS_FIELD, ObjectParser.ValueType.OBJECT);
        PARSER.declareString((request, s) -> {
            request.setScriptType(ScriptType.STORED);
            request.setScript(s);
        }, ID_FIELD);
        PARSER.declareBoolean(SearchTemplateRequest::setExplain, EXPLAIN_FIELD);
        PARSER.declareBoolean(SearchTemplateRequest::setProfile, PROFILE_FIELD);
        PARSER.declareField((parser, request, value) -> {
            request.setScriptType(ScriptType.INLINE);
            if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
                // convert the template to json which is the only supported XContentType (see CustomMustacheFactory#createEncoder)
                try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
                    request.setScript(Strings.toString(builder.copyCurrentStructure(parser)));
                } catch (IOException e) {
                    throw new ParsingException(parser.getTokenLocation(), "Could not parse inline template", e);
                }
            } else {
                request.setScript(parser.text());
            }
        }, SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_STRING);
        PARSER.declareString(SearchTemplateRequest::setProjectRouting, PROJECT_ROUTING_FIELD);
    }

    public static SearchTemplateRequest fromXContent(XContentParser parser) throws IOException {
        return PARSER.parse(parser, new SearchTemplateRequest(), null);
    }

    @Override
    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject();

        if (scriptType == ScriptType.STORED) {
            builder.field(ID_FIELD.getPreferredName(), script);

View on GitHub (pinned to db6a809a66)

Solutions

  1. Validate the 'source' object is well-formed JSON before sending (run it through a JSON linter).
  2. If the template is a simple string, pass it as a VALUE_STRING instead of a START_OBJECT to bypass the copy path.
  3. Ensure the request Content-Type matches the actual body encoding (application/json for a JSON object).
  4. Reproduce locally with a minimal template and bisect to find the offending nested element.

Example fix

// before (malformed object)
{"source":{"query":{"match":{ "title": "star" }}, "params":{}}
// after (valid, and as string form)
{"source":"{\"query\":{\"match_all\":{}}}","params":{}}
Defensive patterns

Strategy: validation

Validate before calling

// Validate inline template object is well-formed JSON before sending
function validateInlineTemplate(body) {
  if (body.source && typeof body.source === 'object') {
    // Re-serialize to confirm it round-trips cleanly
    JSON.stringify(body.source);
  } else if (body.source && typeof body.source === 'string') {
    JSON.parse(body.source); // will throw if malformed string-as-json
  }
  return true;
}

Try / catch

// catch (ElasticsearchStatusException e) where message starts with 'Could not parse inline template' -> surface the body's 'source' for the user to fix

Prevention

When it happens

Trigger: POSTing _search/template with "source" as an object whose content triggers an IOException during XContent copy — e.g. malformed nested structure, an invalid token sequence, or an encoding issue in the stream. The field is declared OBJECT_OR_STRING, so an object form is accepted but must be fully copyable.

Common situations: Hand-constructing a template body with an unterminated object or a stray token. Sending a template originally written in Smile/YAML/CBOR but labeled as JSON. Upgrading a client that changed serialization and now emits a partially-valid object. Network/proxy truncating the request mid-object.

Related errors


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