From 4179ecd469f1065453cd0d57064c7df3a5c6a524 Mon Sep 17 00:00:00 2001 From: Saurabh Lingam <141753821+SaurabhLingam@users.noreply.github.com> Date: Tue, 2 Sep 2025 13:47:34 +0530 Subject: [PATCH] Fix JSON serialization error for ModelMetaclass objects (#9812) - Add robust serialize_for_json() function to handle non-serializable objects - Update server_error_response() to safely serialize exception data - Update get_json_result() with fallback error handling - Handles ModelMetaclass, functions, and other problematic objects - Maintains proper JSON response format instead of server crashes Fixes #9797 ### What problem does this PR solve? Currently, error responses and certain result objects may include types that are not JSON serializable (e.g., ModelMetaclass, functions). This causes server crashes instead of returning valid JSON responses. This PR introduces a robust serializer that converts unsupported types into string representations, ensuring the server always returns a valid JSON response. ### Type of change - [] Bug Fix (non-breaking change which fixes an issue) --- api/utils/api_utils.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/api/utils/api_utils.py b/api/utils/api_utils.py index 9b0b481e3..30cf12aae 100644 --- a/api/utils/api_utils.py +++ b/api/utils/api_utils.py @@ -56,6 +56,30 @@ from rag.utils.mcp_tool_call_conn import MCPToolCallSession, close_multiple_mcp_ requests.models.complexjson.dumps = functools.partial(json.dumps, cls=CustomJSONEncoder) +def serialize_for_json(obj): + """ + Recursively serialize objects to make them JSON serializable. + Handles ModelMetaclass and other non-serializable objects. + """ + if hasattr(obj, '__dict__'): + # For objects with __dict__, try to serialize their attributes + try: + return {key: serialize_for_json(value) for key, value in obj.__dict__.items() + if not key.startswith('_')} + except (AttributeError, TypeError): + return str(obj) + elif hasattr(obj, '__name__'): + # For classes and metaclasses, return their name + return f"<{obj.__module__}.{obj.__name__}>" if hasattr(obj, '__module__') else f"<{obj.__name__}>" + elif isinstance(obj, (list, tuple)): + return [serialize_for_json(item) for item in obj] + elif isinstance(obj, dict): + return {key: serialize_for_json(value) for key, value in obj.items()} + elif isinstance(obj, (str, int, float, bool)) or obj is None: + return obj + else: + # Fallback: convert to string representation + return str(obj) def request(**kwargs): sess = requests.Session() @@ -128,7 +152,11 @@ def server_error_response(e): except BaseException: pass if len(e.args) > 1: - return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=e.args[1]) + try: + serialized_data = serialize_for_json(e.args[1]) + return get_json_result(code= settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=serialized_data) + except Exception: + return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message=repr(e.args[0]), data=None) if repr(e).find("index_not_found_exception") >= 0: return get_json_result(code=settings.RetCode.EXCEPTION_ERROR, message="No chunk found, please upload file and parse it.")