Python3.9使用Telethon报断开连接无法发送请求错误排查
问题场景
Python 3.9环境下使用Telethon库实现批量删除频道、聊天会话功能,运行提供的代码时触发报错:
raise ConnectionError('Cannot send requests while disconnected') ConnectionError: Cannot send requests while disconnected
报错直接触发原因
- 客户端实例管理混乱:
delete_all_dialogs和leave_all_groups两个函数各自调用auth_client()生成独立的TelegramClient实例,其中leave_all_groups内创建的客户端从未执行await client.start()建立连接,直接发起API请求就会触发未连接错误。 - 异步执行逻辑完全错误:
main函数中先通过await执行完两个业务函数,再将两个函数的None类型返回值传入asyncio.gather做并发调度,实际gather没有执行任何有效业务逻辑,还因为两个函数持有独立客户端实例导致连接状态完全不可控。 - 裸异常捕获吞掉前置错误:
delete_all_dialogs中使用无差别except: pass捕获所有异常,把客户端断连、权限不足、API调用失败等错误全部静默,导致前置流程的连接异常无法被提前感知。
代码存在的其他严重问题
- 连接资源泄漏:两个业务函数各自创建客户端实例,既不复用已有连接,也没有在使用完成后主动关闭连接,会造成TCP连接泄漏。
- 群组判断逻辑完全失效:
leave_all_groups中写有判断条件if chat == True:,其中chat是Telegram返回的会话实体对象,永远不可能等于布尔值True,导致群组列表groups始终为空,退群逻辑根本不会触发。 - 会话拉取不全:手动调用
GetDialogsRequest仅拉取第一页最多200个会话,没有实现分页翻页逻辑,账号下会话总数超过200时,剩余会话完全不会被处理。 - API调用参数错误:
LeaveChannelRequest要求传入的channel参数为会话实体/InputPeer对象/数字ID,代码中传入username字符串,会触发参数类型错误。 - 权限参数使用错误:
delete_dialog的revoke=True参数仅对私聊双方会话生效,对群组、频道调用该参数会触发权限错误,普通用户无权撤回群组/频道内的全量历史消息。 - 未过滤特殊会话:代码会尝试删除/退出收藏夹(Saved Messages)、Telegram官方通知等无法主动退出/删除的系统会话,直接触发API报错。
- 事件循环写法过时:Python 3.7+版本可直接使用
asyncio.run()启动协程,不需要手动获取事件循环对象调用run_until_complete。 - 无频率限制防护:批量调用API时没有添加请求间隔,很容易触发Telegram的接口频率限制,导致账号被临时限制请求。
修复方案
修复核心思路:
- 全局仅创建一个
TelegramClient实例,统一管理连接的建立和释放 - 直接使用Telethon内置的
iter_dialogs方法自动处理会话拉取的分页逻辑,不需要手动实现底层GetDialogsRequest调用 - 修正会话类型判断逻辑,跳过无法删除的系统特殊会话
- 针对不同会话类型使用正确的删除/退出参数,移除无效的并发逻辑
- 添加单会话级别的异常捕获,单个会话处理失败不中断整体流程,添加合理的请求间隔避免触发频率限制
修复后的完整可运行代码:
import asyncio from telethon import TelegramClient # 替换为自己账号的API凭证 API_ID = 12345 API_HASH = "0123456789abcdef0123456789abcdef" SESSION_NAME = "main_session" async def main(): # 使用上下文管理器自动管理客户端连接生命周期,退出时自动断开 async with TelegramClient(SESSION_NAME, API_ID, API_HASH) as client: process_count = 0 # 内置iter_dialogs自动处理分页,拉取账号下所有会话 async for dialog in client.iter_dialogs(): process_count += 1 try: # 跳过自己的收藏夹(Saved Messages),该会话无法删除 if dialog.is_user and dialog.entity.id == (await client.get_me()).id: print(f"{process_count} - 跳过收藏夹会话") continue # 处理群组/频道:退出后删除会话,关闭revoke避免权限错误 if dialog.is_group or dialog.is_channel: await client.delete_dialog(dialog, revoke=False) print(f"{process_count} - 已退出并删除群/频道:{dialog.name}") # 处理私聊会话:可根据需求选择是否开启revoke撤回自己发送的历史消息 elif dialog.is_user: await client.delete_dialog(dialog, revoke=True) print(f"{process_count} - 已删除私聊会话:{dialog.name}") else: print(f"{process_count} - 跳过未知类型会话:{dialog.name}") # 添加500ms请求间隔,避免触发API频率限制 await asyncio.sleep(0.5) except Exception as e: print(f"{process_count} - 处理会话[{dialog.name}]失败:{str(e)}") continue if __name__ == "__main__": asyncio.run(main())
内容的提问来源于stack exchange,提问作者2'71ヅ Danger
相关产品推荐
相关产品推荐

