diff --git a/agentscope-core/src/main/java/io/agentscope/core/util/JsonSchemaUtils.java b/agentscope-core/src/main/java/io/agentscope/core/util/JsonSchemaUtils.java
index f15efe5984..703d7a4a8c 100644
--- a/agentscope-core/src/main/java/io/agentscope/core/util/JsonSchemaUtils.java
+++ b/agentscope-core/src/main/java/io/agentscope/core/util/JsonSchemaUtils.java
@@ -53,6 +53,10 @@
*
{@code @JsonClassDescription(...)} - add class description
*
*
+ * All public methods are thread-safe. Schema generation through the shared victools
+ * {@code SchemaGenerator} is serialized by an internal lock, because the generator itself
+ * is not designed for concurrent use.
+ *
* @hidden
*/
public class JsonSchemaUtils {
@@ -61,6 +65,13 @@ public class JsonSchemaUtils {
private static final SchemaGenerator schemaGenerator;
+ /**
+ * Guards the shared victools {@link SchemaGenerator}, which is not thread-safe: its
+ * JacksonModule keeps an unsynchronized introspection cache, so concurrent schema
+ * generation must be serialized.
+ */
+ private static final Object SCHEMA_LOCK = new Object();
+
static {
// JacksonModule to support @JsonProperty, @JsonPropertyDescription annotations
JacksonModule jacksonModule =
@@ -95,7 +106,10 @@ public class JsonSchemaUtils {
*/
public static Map generateSchemaFromClass(Class> clazz) {
try {
- JsonNode schemaNode = schemaGenerator.generateSchema(clazz);
+ JsonNode schemaNode;
+ synchronized (SCHEMA_LOCK) {
+ schemaNode = schemaGenerator.generateSchema(clazz);
+ }
return JsonUtils.getJsonCodec()
.convertValue(schemaNode, new TypeReference