Telethon FloodWaitError: автоматическое ожидание или зависшая публикация?
Воркер начал публикацию, но пост не появился в канале, а обработчик FloodWaitError не срабатывает. Это ещё не означает зависание: Telethon может автоматически выдерживать назначенную сервером паузу, а затем повторять запрос. Пока библиотека обрабатывает ожидание, исключение не доходит до приложения.
Ниже — разбор Telethon 1.45.0 на 29 октября 2026 года: как распознать такую паузу, настроить тайм-аут диагностики и сохранить понятное состояние очереди. Поведение сверено с установленным исходным кодом и официальной документацией. Детали версии проверены в установленных файлах client/users.py, client/telegrambaseclient.py и helpers.py пакета Telethon; ссылки ниже ведут к доступной общей документации.
1. Почему FloodWaitError не доходит до обработчика
Параметр TelegramClient.flood_sleep_threshold по умолчанию равен 60 секундам. При получении FloodWaitError обработчик запросов сравнивает e.seconds с порогом. Если ожидание укладывается в него, библиотека выполняет asyncio.sleep, после чего повторяет запрос в пределах настроенного бюджета попыток. Поэтому отсутствие исключения не подтверждает ни отсутствие ограничения, ни успешную публикацию.
Автоматическая пауза освобождает цикл событий, но текущая корутина продолжает ждать. Если очередь обрабатывает задания последовательно, следующие публикации тоже задержатся. Не запускайте второй воркер только потому, что первый перестал быстро отвечать: сначала определите, выполняет ли он запрос, спит или уже завершился.
У Telegram нет одной универсальной квоты публикаций, которую можно вывести из этого параметра. Порог задаёт поведение клиента при полученном ограничении; разрешённую сервером нагрузку он не увеличивает.
2. Где проходит граница автоматической паузы
В установленной версии 1.45.0 сравнение включительное: e.seconds <= self.flood_sleep_threshold. При пороге 60 ожидание ровно 60 секунд обрабатывается автоматически, а 61 секунда передаётся вызывающему коду как FloodWaitError. Решение определяется длительностью, сообщённой сервером, а не тем, сколько секунд приложение уже ждало ответа.
Значение 0 передаёт управление ожиданием приложению: положительный FloodWait выходит из обработчика запросов. Это удобно для очереди, которая сохраняет срок следующего допустимого действия и освобождает воркер. Ноль не отменяет ограничение и не даёт оснований немедленно отправлять запрос заново.
| Ситуация | Поведение Telethon | Действие приложения |
|---|---|---|
| Порог 60, FloodWait 60 секунд | Автоматическая пауза и повтор в рамках бюджета попыток | Сохранять статус выполнения; не запускать дубль |
| Порог 60, FloodWait 61 секунда | FloodWaitError передаётся наружу | Прочитать e.seconds и отложить задание |
| Порог 0, положительный FloodWait | Ожиданием управляет приложение | Зафиксировать срок ожидания без немедленного повтора |
| Истёк тайм-аут asyncio.wait_for | Запрашивается отмена ожидаемой операции | Дождаться обработки отмены и установить результат исходной операции |

3. Диагностика с тайм-аутом без отправки
Пример использует существующий файл сессии и выполняет только операции чтения в Telegram. Задайте TELEGRAM_SESSION как полный путь с окончанием .session, а TELEGRAM_API_ID и TELEGRAM_API_HASH передайте через окружение. Запускайте после остановки владельца этой сессии. Локальное хранилище Telethon может обновляться: отсутствие отправки сообщений не означает открытия SQLite только для чтения.
Для подключения, проверки авторизации и явного get_me задан общий тайм-аут операции 15 секунд; для ожидания disconnect — отдельные 5 секунд. Это не гарантия завершения программы за 20 секунд: asyncio.wait_for запрашивает отмену и может ждать её обработки дольше. Код не вызывает start, вход или отправку; обработанные ошибки выводятся без текста исключения, только с меткой и именем класса.
request_retries=0 оставляет одну начальную попытку каждого RPC-запроса. Это не обещание отсутствия внутренних задержек: при некоторых серверных ошибках обработчик всё равно выполняет двухсекундный sleep перед исчерпанием попыток. С flood_sleep_threshold=0 и raise_last_call_error=True положительный FloodWait передаётся выше, если его не перехватывает вызываемый метод. Переподключения в примере отключены.
import asyncio
import logging
import os
from pathlib import Path
from telethon import TelegramClient, errors
logging.getLogger("telethon").setLevel(logging.CRITICAL)
async def probe(client):
await client.connect()
if not await client.is_user_authorized():
print("authorization_unconfirmed")
return
me = await client.get_me()
print("get_me_ok" if me is not None else "identity_unconfirmed")
async def main():
try:
session = Path(os.environ["TELEGRAM_SESSION"])
api_id = int(os.environ["TELEGRAM_API_ID"])
api_hash = os.environ["TELEGRAM_API_HASH"]
if session.suffix != ".session" or not session.is_file():
print("existing_session_required")
return
except (KeyError, ValueError, OSError):
print("configuration_error")
return
try:
client = TelegramClient(
str(session), api_id, api_hash,
flood_sleep_threshold=0,
request_retries=0,
connection_retries=0,
auto_reconnect=False,
receive_updates=False,
raise_last_call_error=True,
)
except Exception as exc:
print("constructor_error", type(exc).__name__)
return
try:
await asyncio.wait_for(probe(client), timeout=15)
except errors.FloodWaitError as exc:
print("flood_wait_seconds", exc.seconds)
except errors.SlowModeWaitError as exc:
print("slow_mode_wait_seconds", exc.seconds)
except TimeoutError:
print("diagnostic_timeout")
except errors.RPCError as exc:
print("rpc_error", type(exc).__name__)
except Exception as exc:
print("diagnostic_error", type(exc).__name__)
finally:
try:
await asyncio.wait_for(client.disconnect(), timeout=5)
except Exception as exc:
print("disconnect_error", type(exc).__name__)
if __name__ == "__main__":
asyncio.run(main())В Telethon 1.45.0 disconnect возвращает задачу очистки, защищённую asyncio.shield. Вывод disconnect_error TimeoutError означает, что срок ожидания истёк, а завершение очистки не подтверждено. Защищённая задача может продолжать работу; при завершении asyncio.run оставшиеся задачи отменяются. Успешное отключение и общая длительность программы этим тайм-аутом не гарантируются.
is_user_authorized перехватывает RPCError и может вернуть False также при ошибке проверки. Поэтому authorization_unconfirmed требует разбора, а не автоматического нового входа. get_me_ok подтверждает получение собственного пользователя, но не права публикации в канал и не отсутствие ограничений у отправки.
Среда исходной проверки — Python 3.14.6 и Telethon 1.45.0. Проверки примера ограничены синтаксисом и офлайн-моками; реальное подключение, авторизация и работа Telegram этим материалом не подтверждаются.
4. Какие логи подтверждают автоматическое ожидание
Для расследования в рабочем воркере включите INFO у логгера telethon.client.users и настройте принимающий его записи обработчик логов с уровнем INFO или ниже. Одного уровня логгера недостаточно. В обработчике FloodWait этой версии есть сообщение о длительности сна и типе запроса. Сопоставьте его с событиями начала и завершения операции. Пример выше подавляет обычный библиотечный вывод.
Записывайте внутреннюю метку задания, имя RPC-метода, текущий порог, прошедшее время по монотонным часам и конечный статус. Не выводите объект запроса или ответа целиком, ID пользователя или канала, телефон, api_hash, содержимое сообщения и сессии. Запись об ожидании объясняет паузу; последующий результат подтверждает исход запроса.
Если внешний тайм-аут короче серверного ожидания, он может прервать автоматическую паузу. Параметр timeout конструктора относится к подключению и не заменяет asyncio.wait_for вокруг всей операции. Согласуйте бюджет ожидания с правилами очереди, учитывая время обработки отмены.
5. Как вернуть очереди понятное состояние
FloodWaitError требует учесть e.seconds и сохранить срок ожидания. SlowModeWaitError относится к интервалу сообщений конкретного чата и также учитывается автоматическим порогом. PeerFloodError — другой сигнал ограничения: его нельзя превращать в таймер с гарантированным сроком восстановления. При нём остановите затронутые отправки и разберите причину.
TimeoutError означает превышение временного бюджета, а не доказанное ограничение Telegram. Причиной может быть сеть или длительное выполнение; при отправке результат иногда остаётся неизвестным. Сначала сверьте исходную операцию и фактическую публикацию, затем решайте вопрос повтора. До сверки сохраните статус «результат неизвестен».
В собственной очереди различайте выполнение, ожидание до указанного срока, подтверждённое завершение и неизвестный результат. Для редактора, работающего через интерфейс Telegram, отдельно разобраны изменение и отмена нативных отложенных постов. Планирование материалов и обработку ответов рассматривает руководство по маркетинговой кампании в Telegram.
PSYKE поддерживает отдельную задачу проверки номеров. Проверки номеров Telegram не определяют поведение вашего Telethon-клиента или право писать в канал. Практический следующий шаг разработчика — сопоставить порог, тайм-аут и статусы очереди, а после неоднозначного исхода сверить публикацию.
Частые вопросы
Почему except FloodWaitError не срабатывает, хотя публикация задерживается?
При стандартном flood_sleep_threshold=60 Telethon автоматически обрабатывает ожидание до 60 секунд включительно и повторяет запрос в рамках бюджета попыток. Проверьте INFO-логи telethon.client.users, настройку обработчика логов и последующий результат операции.
Можно ли сразу повторить отправку после тайм-аута?
Сначала установите результат исходной операции: публикация могла состояться до потери ответа. Сохраните неопределённый статус и сверьте канал и учёт задания. Новый запуск без сверки может создать дубль.