You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用python-socket时出现RecursionError及UUID序列化问题求助

Python Socket.IO 遇到 RecursionError 求助

Socket配置无问题,简单测试事件可正常运行,但在返回包含payload的响应时触发RecursionError,注释掉"payload": res["data"]后代码运行正常。

相关代码

Socket事件监听代码

@sio_server.on(EVENT.REPLY_ON_TICKET)
async def reply_on_ticket_event(sid:str, data:dict):
    
    sender_id = None
    if data["sender_info"]["sender_id"]:
        sender_id= data["sender_info"]["sender_id"]
    else:
        sender_id= data["sender_id"]
        
    payload = {
        "ticket_id": data["ticket_id"],
        "sender_id": sender_id,
        "notify": data["notify"],
        "reply": data["reply"],
    }
    # Obtain a database session manually
    db_session = next(get_db())
    res = await reply_on_ticket_service(payload, db_session)
    if res["success"]:
        # the callback data for client socket
        return {
                    "status_code": 200,
                    "success": True,
                    "message": "reply data",
                    "payload": res["data"],
                }
    
    return {"status_code": 400, "success": False}  

工单回复逻辑服务代码

async def reply_on_ticket_service(data:dict, db_session: Session = Depends(get_db)):
    try:
        new_reply= TicketReplyModal(reply_text=data["reply"],ticket_id=data["ticket_id"],sender_id=data["sender_id"])
        
        new_reply_id= await db_manager.add_record(new_reply,db_session)
        if not new_reply_id:
            return {"success":False, 'status_code':400, "message":"Error occurred while creating new replay"} 
        new_record= await db_manager.get_record_by_id(new_reply_id, TicketReplyModal,db_session)
        if not new_record:
            return {"success":False, 'status_code':404, "message":"Unable to find record with newly created replay"} 
            
        sender_data = db_session.query(UserModel).filter(UserModel.id == new_record.sender_id).first()
        if not sender_data:
            return {"success":False, 'status_code':404, "message":"Unable to find sender info"} 
        
        reply = {
            "id":new_record.id,
            "created_at":new_record.created_at,
            "reply_text":new_record.reply_text,
            "ticket_id": new_record.ticket_id,
            "sender":{
                "id": sender_data.id,
                "email": sender_data.email,
                "profile_image": sender_data.profile_image,
                "username": sender_data.username,
                "public_key": sender_data.public_key,
            }

        }
        
        return {"success": True, "data": reply}
        

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

自定义JSON编码器代码(错误根源)

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            return str(obj)
        return super().default(obj)
    

@app.on_event("startup")
def startup_event():
    json.JSONEncoder.default = CustomJSONEncoder().default

错误信息

RecursionError日志

Task exception was never retrieved
future: <Task finished name='Task-39' coro=<AsyncServer._handle_event_internal() done, defined at C:\Users\AK\.virtualenvs\BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py:605> exception=RecursionError('maximum recursion depth exceeded')>
Traceback (most recent call last):
  File "C:\Users\AK\.virtualenvs\BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py", line 617, in _handle_event_internal
    await server._send_packet(eio_sid, self.packet_class(
  File "C:\Users\AK\.virtualenvs\BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py", line 518, in _send_packet
    encoded_packet = pkt.encode()
                     ^^^^^^^^^^^^
  File "C:\Users\AK\.virtualenvs\BE-hVO0_cLf\Lib\site-packages\socketio\packet.py", line 64, in encode
    encoded_packet += self.json.dumps(data, separators=(',', ':'))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "D:\BE\app.py", line 38, in default
    return super().default(obj)
           ^^^^^^^^^^^^^^^^^^^^
  File "D:\BE\app.py", line 38, in default
    return super().default(obj)
           ^^^^^^^^^^^^^^^^^^^^
  File "D:\BE\app.py", line 38, in default
    return super().default(obj)
           ^^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 972 more times]
RecursionError: maximum recursion depth exceeded

注释编码器后的UUID序列化错误

Task exception was never retrieved
future: <Task finished name='Task-47' coro=<AsyncServer._handle_event_internal() done, defined at C:\Users\AK\.virtualenvs\pw7-BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py:605> exception=TypeError('Object of type UUID is not JSON serializable')>
Traceback (most recent call last):
  File "C:\Users\AK\.virtualenvs\pw7-BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py", line 617, in _handle_event_internal
    await server._send_packet(eio_sid, self.packet_class(
  File "C:\Users\AK\.virtualenvs\pw7-BE-hVO0_cLf\Lib\site-packages\socketio\async_server.py", line 518, in _send_packet
    encoded_packet = pkt.encode()
                     ^^^^^^^^^^^^
  File "C:\Users\AK\.virtualenvs\pw7-BE-hVO0_cLf\Lib\site-packages\socketio\packet.py", line 64, in encode
    encoded_packet += self.json.dumps(data, separators=(',', ':'))
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\__init__.py", line 238, in dumps
    **kw).encode(obj)
          ^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\encoder.py", line 200, in encode
    chunks = self.iterencode(o, _one_shot=True)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\encoder.py", line 258, in iterencode
    return _iterencode(o, 0)
           ^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python311\Lib\json\encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type UUID is not JSON serializable

问题原因

自定义JSON编码器的实现方式错误:将CustomJSONEncoder实例的default方法赋值给json.JSONEncoder.default后,在CustomJSONEncoder.default中调用super().default(obj)时,super()指向的父类JSONEncoder的default方法已经被替换成了当前的CustomJSONEncoder.default,导致无限递归调用,最终触发RecursionError。

解决方案

方案1:给Socket.IO指定自定义JSON序列化函数(推荐)

不需要全局修改JSON编码器,直接在Socket.IO初始化时指定自定义的序列化方法,避免全局污染:

import json
import uuid
from socketio import AsyncServer

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            return str(obj)
        return super().default(obj)

# 定义自定义的dumps函数
def custom_json_dumps(data):
    return json.dumps(data, cls=CustomJSONEncoder)

# 初始化Socket.IO服务器时传入json参数
sio_server = AsyncServer(json=custom_json_dumps)

方案2:正确全局替换JSONEncoder.default

保存原始的default方法,避免递归调用:

import json
import uuid

# 保存原始的default方法
original_default = json.JSONEncoder.default

def custom_default(obj):
    if isinstance(obj, uuid.UUID):
        return str(obj)
    # 调用原始的default方法处理其他类型
    return original_default(obj)

@app.on_event("startup")
def startup_event():
    json.JSONEncoder.default = custom_default

内容的提问来源于stack exchange,提问作者Abidullah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 13:04:49