Skip to content

[Bug]:Schema Validation Error with Reused Objects in Structured Output #1176

@killer9527

Description

@killer9527

Bug Report: Schema Validation Error with Reused Objects in Structured Output

AgentScope-Java is an open-source project. To involve a broader community, we recommend asking your questions in English.

Describe the bug

When using structured output with reused objects (i.e., the same class used multiple times in a parent class), the framework generates a JSON Schema with $defs and $ref references. However, during parameter validation, the schema validator fails to resolve these references, resulting in a Schema validation error: Reference /$defs/Address cannot be resolved error.

This prevents the use of reusable object structures in structured output, which is a common and important pattern for complex data extraction scenarios.

To Reproduce

Steps to reproduce the behavior:

1. Your code

Create a structured output class with reused objects:

/** Schema for order information with address reuse. */
public static class OrderInfo {
    public String orderId;
    public String orderStatus;
    public String customerName;
    public String customerPhone;
    public String customerEmail;
    public Address billingAddress;      // Reused Address class
    public Address shippingAddress;     // Reused Address class (same as billingAddress)
    public List<OrderItem> items;
    public Double subtotal;
    public Double tax;
    public Double shippingFee;
    public Double totalAmount;
    public String paymentMethod;
    public String createdAt;
    public String updatedAt;

    public OrderInfo() {}
}

/** Address information - reused in billing and shipping. */
public static class Address {
    public String province;
    public String city;
    public String district;
    public String street;
    public String postalCode;
    public String recipientName;
    public String recipientPhone;

    public Address() {}
}

/** Order item. */
public static class OrderItem {
    public String productName;
    public String productId;
    public Integer quantity;
    public Double unitPrice;
    public Double subtotal;

    public OrderItem() {}
}

Use the class with ReActAgent:

/** Example 5: Extract order information with nested structures. */
    private static void runOrderExtractionExample(ReActAgent agent) {
        String query =
                "Order ID: ORD20260409001, Customer: Zhang San (Phone: 13800138000, Email: zhangsan@example.com). "
                        + "Shipping Address: 88 Jianguo Road, Chaoyang District, Beijing, Postal Code 100022, Recipient: Li Si, Phone: 13900139000. "
                        + "Billing Address: 1 Zhongguancun Street, Haidian District, Beijing, Postal Code 100080, Recipient: Zhang San, Phone: 13800138000. "
                        + "Ordered Items: iPhone 15 Pro (Product ID: IP15PRO) 2 units, Unit Price: 8999 CNY, Subtotal: 17998 CNY; "
                        + "AirPods Pro (Product ID: APPRO) 1 unit, Unit Price: 1899 CNY, Subtotal: 1899 CNY. "
                        + "Subtotal: 19897 CNY, Tax: 1193.82 CNY, Shipping Fee: 0 CNY, Total: 21090.82 CNY. "
                        + "Payment Method: Alipay. Order Status: Paid. "
                        + "Created At: 2026-04-09T10:30:00+08:00, Updated At: 2026-04-09T11:00:00+08:00.";

        System.out.println("Order Description: " + query);
        System.out.println("\nExtracting order information...\n");

        Msg userMsg =
                Msg.builder()
                        .role(MsgRole.USER)
                        .content(
                                TextBlock.builder()
                                        .text("Extract order information from the following text: " + query)
                                        .build())
                        .build();

        try {
            Msg msg = agent.call(userMsg, OrderInfo.class).block();
            OrderInfo result = msg.getStructuredData(OrderInfo.class);

            System.out.println("========== Extracted Order Information ==========");

        } catch (Exception e) {
            System.err.println("错误: " + e.getMessage());
            e.printStackTrace();
        }
    }

    

2. How to execute

Run the code with a valid LLM model configuration. The error occurs during the agent's tool execution phase when it tries to validate the structured output schema.

3. See error

14:17:51.702 [boundedElastic-1] DEBUG io.agentscope.core.tool.ToolExecutor - Parameter validation failed for tool 'generate_response': Schema validation error: Reference /$defs/Address cannot be resolved

Expected behavior

The framework should correctly handle JSON Schema references ($defs and $ref) during parameter validation. When a class is reused multiple times in a structured output, the generated schema should properly define the reusable type in $defs and reference it using $ref, and the validator should be able to resolve these references.

The expected behavior is:

  1. The framework generates a JSON Schema with $defs for reused classes
  2. The schema validator correctly resolves $ref references to $defs
  3. The structured output extraction works correctly with reused objects

Environment (please complete the following information):

  • AgentScope-Java Version: 1.0.11
  • Java Version: 21 (also tested with 17)
  • OS: macOS

Additional context

Root Cause Analysis

The issue appears to be in the schema validation logic within io.agentscope.core.tool.ToolExecutor. When the JSON Schema generator (likely using a library like jsonschema-generator) creates schemas with $defs for reused classes, the validator is unable to resolve the $ref references.

This is a critical issue because:

  1. Code Reusability: Reusing classes in structured output is a common pattern to avoid duplication
  2. Schema Complexity: Complex real-world scenarios often require reused structures (e.g., billing/shipping addresses, multiple contact persons, etc.)
  3. JSON Schema Best Practices: Using $defs and $ref is the recommended approach in JSON Schema specification for avoiding duplication

Deeper Analysis: Schema Structure Issue

After further investigation, the root cause appears to be that the reused object definitions ($defs) are not being defined at the top level of the generated schema JSON.

Actual Generated Schema (from debug logs):

The following is the actual schema generated by the framework (formatted for readability):

{
  "type": "object",
  "properties": {
    "response": {
      "$defs": {
        "Address": {
          "type": "object",
          "properties": {
            "city": {"type": "string"},
            "district": {"type": "string"},
            "postalCode": {"type": "string"},
            "province": {"type": "string"},
            "recipientName": {"type": "string"},
            "recipientPhone": {"type": "string"},
            "street": {"type": "string"}
          }
        }
      },
      "type": "object",
      "properties": {
        "billingAddress": {"$ref": "#/$defs/Address"},
        "shippingAddress": {"$ref": "#/$defs/Address"},
        "orderId": {"type": "string"},
        "orderStatus": {"type": "string"},
        "customerName": {"type": "string"},
        "customerPhone": {"type": "string"},
        "customerEmail": {"type": "string"},
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "productName": {"type": "string"},
              "productId": {"type": "string"},
              "quantity": {"type": "integer"},
              "unitPrice": {"type": "number"},
              "subtotal": {"type": "number"}
            }
          }
        },
        "subtotal": {"type": "number"},
        "tax": {"type": "number"},
        "shippingFee": {"type": "number"},
        "totalAmount": {"type": "number"},
        "paymentMethod": {"type": "string"},
        "createdAt": {"type": "string"},
        "updatedAt": {"type": "string"}
      }
    }
  },
  "required": ["response"]
}

This is a critical issue because:

  1. Code Reusability: Reusing classes in structured output is a common pattern to avoid duplication
  2. Schema Complexity: Complex real-world scenarios often require reused structures (e.g., billing/shipping addresses, multiple contact persons, etc.)
  3. JSON Schema Best Practices: Using $defs and $ref is the recommended approach in JSON Schema specification for avoiding duplication
  4. Schema Validation: JSON Schema validators expect $defs to be at the root level of the schema for proper reference resolution

Workaround

Currently, the only workaround is to avoid reusing classes by creating duplicate classes for each usage:

// Instead of reusing Address, create separate classes
public static class BillingAddress {
    public String province;
    public String city;
    // ... duplicate all fields
}

public static class ShippingAddress {
    public String province;
    public String city;
    // ... duplicate all fields
}

This workaround is not ideal as it leads to code duplication and maintenance issues.

Suggested Fix

The schema validator should be configured to properly resolve JSON Schema references. This might involve:

  1. Using a JSON Schema validator library that supports $ref resolution
  2. Configuring the validator with a proper schema loader that can resolve references
  3. Pre-processing the schema to inline all references before validation

Related Libraries

The project uses:

  • jsonschema-generator (version 4.38.0) for schema generation
  • json-schema-validator (version 3.0.1) for validation

Both libraries support $defs and $ref, so the issue is likely in how they are integrated in AgentScope.

Impact

This bug affects any use case that requires:

  • Multiple addresses (billing, shipping, etc.)
  • Multiple contact persons (customer, recipient, etc.)
  • Any other scenario where the same data structure is used multiple times

It significantly limits the practical applicability of structured output in real-world scenarios.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions