elastic/elasticsearch · error · MustacheException
Failed to convert object to JSON
Error message
Failed to convert object to JSON
What it means
Thrown by ToJsonCode.createFunction() when an IOException occurs while serializing a resolved parameter value to JSON using XContentBuilder. The toJson function attempts to render Iterables as JSON arrays and Maps as JSON objects; if the XContentBuilder encounters a serialization failure (e.g., an unserializable object type), it throws IOException which is wrapped in this MustacheException.
Source
Thrown at modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java:246
if (resolved == null) {
return null;
}
try (XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent())) {
if (resolved instanceof Iterable) {
builder.startArray();
for (Object o : (Iterable<?>) resolved) {
builder.value(o);
}
builder.endArray();
} else if (resolved instanceof Map) {
builder.map((Map<String, ?>) resolved);
} else {
// Do not handle as JSON
return oh.stringify(resolved);
}
return Strings.toString(builder);
} catch (IOException e) {
throw new MustacheException("Failed to convert object to JSON", e);
}
};
}
static boolean match(String variable) {
return CODE.equalsIgnoreCase(variable);
}
}
/**
* This function concatenates the values of an {@link Iterable} using a given delimiter
*/
private static class JoinerCode extends CustomCode {
protected static final String CODE = "join";
private static final String DEFAULT_DELIMITER = ",";
private final String delimiter;View on GitHub (pinned to db6a809a66)
Solutions
- Ensure the parameter passed to {{#toJson}} is a simple type (String, Number, Boolean), a List of simple types, or a Map with String keys and simple values
- Check the params object in the search template request for any custom or non-serializable types
- If passing a Map, ensure all keys are Strings and all values are JSON-compatible
- Test the parameter serialization independently with a JSON serializer to isolate the failing value
Example fix
// before — param with non-serializable nested object:
{"params": {"filter": {"geojson": <complex geojson object>}}}
// after — use simple types only:
{"params": {"filter": ["tag1", "tag2", "tag3"]}} Defensive patterns
Strategy: validation
Validate before calling
// Validate params before using toJson — ensure JSON-serializable types
void validateParams(Map<String, Object> params) {
for (var entry : params.entrySet()) {
Object v = entry.getValue();
if (!(v == null || v instanceof String || v instanceof Number || v instanceof Boolean
|| v instanceof List || v instanceof Map)) {
throw new IllegalArgumentException("Param [" + entry.getKey() + "] is not JSON-serializable");
}
}
} Type guard
boolean isJsonSerializable(Object o) {
return o == null || o instanceof String || o instanceof Number
|| o instanceof Boolean || o instanceof List || o instanceof Map;
} Prevention
- Only pass simple types, Lists, and Maps to {{#toJson}} variables
- Avoid custom Java objects as template parameters
- Test parameter serialization independently before template execution
When it happens
Trigger: Using {{#toJson}}var{{/toJson}} where 'var' resolves to an object that XContentBuilder cannot serialize. For example, a parameter value that is a custom Java type, a nested structure containing nulls in unexpected positions, or a type that falls through to oh.stringify() and fails. Also possible if the resolved value is neither Iterable nor Map but the object handler's stringify fails.
Common situations: Passing complex nested objects as template params where some leaf values are not JSON-serializable. Passing a parameter that is a Set or other non-List Iterable with elements that XContentBuilder rejects. Version mismatches where a parameter type changes between client and server.
Related errors
- Mustache function [{}] must contain one and only one identif
- Unable to encode value
- No encoder found for media type [{}]
- Cannot expand '%s' because partial templates are not support
- Cannot expand '%s' because dynamic partial templates are not
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/24fbc5659480533a.
Report an issue: GitHub.