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
- Validate the 'source' object is well-formed JSON before sending (run it through a JSON linter).
- If the template is a simple string, pass it as a VALUE_STRING instead of a START_OBJECT to bypass the copy path.
- Ensure the request Content-Type matches the actual body encoding (application/json for a JSON object).
- 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
- Prefer the string form of 'source' when the template is already JSON text.
- Set Content-Type: application/json explicitly on the request.
- Lint the template object in CI for stored templates.
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
- project_routing already set
- Malformed search template
- Unknown key for a VALUE_STRING in [project_routing]
- [rest_total_hits_as_int] cannot be used if the tracking of t
- [{}] Expected START_OBJECT but was: {}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/c2abc44fd508d6b5.
Report an issue: GitHub.