{"record":{"id":"24fbc5659480533a","repo":"elastic/elasticsearch","slug":"failed-to-convert-object-to-json","errorCode":null,"errorMessage":"Failed to convert object to JSON","messagePattern":"Failed to convert object to JSON","errorType":"exception","errorClass":"MustacheException","httpStatus":null,"severity":"error","filePath":"modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java","lineNumber":246,"sourceCode":"                if (resolved == null) {\n                    return null;\n                }\n                try (XContentBuilder builder = XContentBuilder.builder(XContentType.JSON.xContent())) {\n                    if (resolved instanceof Iterable) {\n                        builder.startArray();\n                        for (Object o : (Iterable<?>) resolved) {\n                            builder.value(o);\n                        }\n                        builder.endArray();\n                    } else if (resolved instanceof Map) {\n                        builder.map((Map<String, ?>) resolved);\n                    } else {\n                        // Do not handle as JSON\n                        return oh.stringify(resolved);\n                    }\n                    return Strings.toString(builder);\n                } catch (IOException e) {\n                    throw new MustacheException(\"Failed to convert object to JSON\", e);\n                }\n            };\n        }\n\n        static boolean match(String variable) {\n            return CODE.equalsIgnoreCase(variable);\n        }\n    }\n\n    /**\n     * This function concatenates the values of an {@link Iterable} using a given delimiter\n     */\n    private static class JoinerCode extends CustomCode {\n\n        protected static final String CODE = \"join\";\n        private static final String DEFAULT_DELIMITER = \",\";\n\n        private final String delimiter;","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/lang-mustache/src/main/java/org/elasticsearch/script/mustache/CustomMustacheFactory.java#L228-L264","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before — param with non-serializable nested object:\n{\"params\": {\"filter\": {\"geojson\": <complex geojson object>}}}\n\n// after — use simple types only:\n{\"params\": {\"filter\": [\"tag1\", \"tag2\", \"tag3\"]}}","handlingStrategy":"validation","validationCode":"// Validate params before using toJson — ensure JSON-serializable types\nvoid validateParams(Map<String, Object> params) {\n    for (var entry : params.entrySet()) {\n        Object v = entry.getValue();\n        if (!(v == null || v instanceof String || v instanceof Number || v instanceof Boolean\n              || v instanceof List || v instanceof Map)) {\n            throw new IllegalArgumentException(\"Param [\" + entry.getKey() + \"] is not JSON-serializable\");\n        }\n    }\n}","typeGuard":"boolean isJsonSerializable(Object o) {\n    return o == null || o instanceof String || o instanceof Number\n        || o instanceof Boolean || o instanceof List || o instanceof Map;\n}","tryCatchPattern":null,"preventionTips":["Only pass simple types, Lists, and Maps to {{#toJson}} variables","Avoid custom Java objects as template parameters","Test parameter serialization independently before template execution"],"tags":["elasticsearch","lang-mustache","tojson","serialization","template"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}