Ошибки в ботах и как их читать
LatandХочешь научиться писать ботов? Проходи и смотри бесплатные уроки на площадке Botfather.Dev
В первую очередь, давайте разделим 3 типа ошибок:
- Ошибки в Python - например, когда вы делите на ноль
- Ошибки при работе с Telegram Bot API - например, когда вы пытаетесь отправить сообщение ботом несуществующему пользователю
- Прочие ошибки, при работе с другими библиотеками.
Когда возникает ошибка в коде, вы ее узнаете (обычно) по красному цвету текста и кучи строк, которые вы не планировали выводить. Мы рассмотрим наиболее часто повторяющиеся ошибки. Но, возможно вашей ошибки тут не будет.
Сначала мы научимся читать ошибки, чтобы понимать, что вообще с ними можно делать, а потом разберем типичные ошибки с Python и в Telegram bot API и как их решать.
Содержание статьи
├── Как читать код ошибки? ├── Что писать в чат, если возникла ошибка? ├── Типичные ошибки в Python ├── Типичные ошибки при работе с Telegram Bot API ├── Прочие ошибки
Как читать код ошибки?
Лучше всего это показать на примере. Эту ошибку прислал один из студентов, мы её и разберем. Как обычно, много красного текста, но! Это не должно вас пугать.
Самое важное - надо определить, что это таки именно ошибка, а не что-то другое, а это можно сделать по слову Traceback где-то вначале красного текста. Если там такое есть - это ошибка.
Тут и начинается код ошибки. Traceback - ("trace", "back") с английского "отследить" и "назад". Т.е. прослеживаем откуда эту ошибка пришла.
Смотрим сверху вниз, сначала в файле (слово File) по пути D:\Python\aiogram-bot-template
.... и потом смотрим в конец строки
Видим в конце
dispatcher.py, line 388, in _process_polling_updates
Это то, откуда эта ошибка появилась позже всего и на чём всё сломалось. Но это не то место, где вы допустили ошибку! Это файл dispatcher.py
, который лежит по определенному пути, и в нем на 388 строке, в функции _process_polling_updates
(обработка обновлений поллинга) возникла ошибка ошибка в каком-то таком месте:
Все еще сложно понять, что это такое. Вы это не писали? Точно?
Странно... Я тоже этого не писал.
А! Там же ниже еще есть код, оказывается тут выполнялся код функции
await self.process_updates(updates, fast)
А внизу есть код ошибки и этой функции!
Смотрим туда. Там пишет, что ошибка в том же dispatcher.py, в котором выполнялся код функции:
await asyncio.gather(*tasks)
Ну это то точно вы писали! Нет? Уверены???
Значит смотрим ниже.
Дальше идет выполнение какой-то функции:
handler_obj.handler(*args, **partial_data)
В строке 117, в функции notify.
Ну, короче вы поняли, смотрим дальше, пока не появится что-то знакомое.
А! нашел!
Папка, где ученик хранит свой проект называется "Проект", в нем есть папка aiogram-bot-template. Похоже на то.
И тут смотрим как обычно на то, где ошибка возникла, где-то в обоих файлах:
menu_handlers db_commands
Внимательно!
Сначала выполнилась функция navigate на строке 118, потом list_tiker на строке 74, а потом update_userstiker, в которой выполнилась функция:
await user_tiker.create()
Вот оно! Вот это место, где ученик допустил ошибку. Но не похоже на то, что тут что-то сделано не так. Да и внизу есть какой-то код. Но в любом случае, это 100% то место, где ученик допустил ошибку.
Внизу можно прочитать следующее:
Ошибка RuntimeError: cannot reuse already awaited coroutine
. Если английский вам не сильно знаком, а с питоном вы еще пытаетесь подружиться, но до асинхронности еще не дошли, то для вас это будет полной белибердой.
Пытаемся решить ошибку...
Для начала, пробуем прочитать ошибку и перевести ее. Очень часто в самой ошибке содержится уже её решение.
Если это не помогло - пробуем копировать ошибку и просто-напросто ГУГЛИТЬ! ДА!
https://stackoverflow.com/questions/51116849/asyncio-await-coroutine-more-than-once-periodic-tasks
Вот например, один из примеров ответа на эту ошибку. Чаще всего ответы вы найдете на stackoverflow практически на все ошибки!
Если там на английском - не страшно, переводите страницу, благо Google Chrome позволяет это делать максимально просто. А еще лучше, сначала попробуйте саму ошибку перевести!
На том посте видим ответ с галочкой:
Тут говорится, что возможно человек путает асинхронные функции с корутинами (сопрограммами). В данном примере sample
- функция (или ссылка) на асинхронную функцию, а sample()
- уже корутина. И, чтобы забрать данные из корутины, и ее выполнить, необходимо сделать сначала await перед ней.
Почитав немного других постов я понял, что загвоздка в том, что корутину можно await-ить только один раз! Сделать await sample
можно всего один раз.
Значит можно сделать предположение, что user_tiker, перед которым стоит await уже был ранее await-нут. Но, на этом мои догадки заканчиваются...
Ах да, тут ученику, по этой ошибке подсказали:
А вот тут похоже, что сначала происходит await id_tiker и потом он передается в User_Tiker. Не уверен в чем тут дело, но возможно gino пытается внутри что-то await-ить, а может быть ученик сделал что-то, но не показал, и поэтому мы можем только гадать в чем была проблема, потому что позже он написал:
Ну, главное, что решил!
Ах да, мы ж тут не конкретную ошибку разбирали, а возможный путь её решения.
Подытожим:
- Ищем Traceback
- Читаем его и пытаемся найти то последнее место в коде, где ВЫ могли допустить ошибку. То есть в том, что писали именно вы.
- В крайнем случае - обращаемся к концу кода и читаем именно Exception или Error.
- Гуглим этот Exception/Error и ищем пути решения.
- Отслеживаем в других функциях выше, где вы могли допустить ошибку.
- Если ничего не находите дельного - пишите в чаты.
Я не нашел решения! Что писать в чат, когда возникла ошибка?
Очень просто. Есть несколько важных моментов:
- Пишите вежливо, никто не обязан за вас решать ваши ошибки, но кто-то найдется, чтобы помочь.
- Опишите проблему, если можно, в одном сообщении.
- Опишите то, что вы пытались сделать, и какие у вас предположения! Никто не любит помогать тем, кто сразу пишет в чат и не гуглит.
- Залейте ВЕСЬ Traceback на pastebin.com или dpaste.org
- Можете добавить часть вашего кода, где у вас возникает ошибка, берите функции и строки из трейсбека + 10-20 строк по вниз и вверх, чтобы был понятен контекст проблемы.
Ошибки в Python
Ошибок в Python может быть очень много и зависит от контекста. Для того, чтобы они не возникали - надо учить питон. Ага, все таки придется...
Почитать о них всех можно тут: https://docs.python.org/3/library/exceptions.html#concrete-exceptions
Те, с которыми часто сталкиваются:
SyntaxError - ошибка при работе с синтаксисом Python
Пожалуй, самая распространенная ошибка... Если бы все начинали не с ботов, а с изучения языка программирования Python, то было бы лучше!
Она возникает, когда вы просто неправильно ввели код. Когда возникает?
- Опечатались при создании функции/класса. Ввели не
def dunc():
, а
defc func():
Или
clas MyClass():
, вместо
class MyClass():
- Не поставили скобочки/двоеточие/знак равно, где нужно. Например, при создании функции:
def a: return 1
- Не закрыли скобки
- Не поставили кавычки, когда записываете текст в переменную:
BOT_TOKEN = 1223423543:IOFNGVOIWENOGIBWENIOGEWRNGVEW
- Используете зарезервированные имена функций в питоне
ImportError - ошибка при работе с импортами.
- Ошибка импорта библиотеки, очень часто при цикличном импорте. То есть, вы в файле
a
, импортируете функцию из модуляb
. При этом, в модулеb
у вас что-то импортируется из модуляa
. И питон начинать ходить по кругу и импортировать одно из другого (нет).
Сделайте так, чтобы не пришлось из модуля a
импортировать что-то в b
. Переместите эту функцию в модуль c
.
- А еще бывает такое:
Если вы запускаете код бота, но не из того модуля, где идет запуск поллинга, а где просто обозначены хендлеры. Этот файл не надо запускать.
ModuleNotFoundError - не установлена библиотека.
Да. Не установлена. Установите ее с помощью pip install <имя библиотеки>
, но перед этим убедитесь, что вы ввели правильное название ее из pypi
. Названия можно глянуть тут: https://pypi.org/project/pip/
- А еще, может быть просто вы пытаетесь импортировать файл из своего проекта, но он лежит в другой папке
TypeError - ошибка при работе с типами данных.
Это может произойти, когда вы используете неправильные методы для работы с объектами. Например, вы попытались сложить текст с числом.
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Но еще может возникнуть, когда вы работаете с вызовом функций:
TypeError: func() takes 1 positional argument but 2 were given
В функцию передаете больше аргументов, чем она ждет.
TypeError: func() got multiple values for argument 'a'
Когда вы передаете в функцию несколько значений для одной переменной.
TypeError: func() missing 1 required positional argument: 'a'
Когда вы в функцию не передали нужные аргументы, например positional - те, что идут по порядку
TypeError: my_func() missing 1 required positional argument "self"
Это довольно частая ошибка, когда вы работаете с классом, который не инициализирован.
Например, есть класс Bot
, у него есть метод get_me
, вы пытаетесь запустить функцию Bot.get_me()
. И, несмотря на то, что класс имеет этот метод, пока вы не выполните функцию __init__
, в которой создастся self
, у вас будет эта ошибка.
Функция инит выполняется когда вы создаете экземпляр класса. Как-то так: bot = Bot()
. Только после этого можно сделать bot.get_me()
TypeError: object function can't be used in `await` expression
Похоже, что кто-то забыл, что await-ить можно только асинхронные функции, а не обычные. Либо уберите await
, либо сделайте функцию асинхронной, с помощью приставки async: async def my_func(): ...
- TypeError: "coroutine" object is not subscriptable
Тут ошибка говорит, что мы не можем достать элемент из корутины. Типа если сделать await bot.get_me()["username"]
. Вообще, есть порядок выполнения кода, и тут сначала питон пытается достать элемент по ключу username из корутины bot.get_me()
, а потом только он сделает await.
В данном случае, нам надо сначала сделать await, потому мы заключим этот фрагмент в скобки:
(await bot.get_me())["username"]
И тогда все выполнится.
Но ошибка заключается в том, что данный тип объекта не позволяет брать элементы таким способом ["element"]
, или по индексу [2]
.
RuntimeWarning: coroutine 'my_func' was never awaited
... RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Не совсем ошибка, но все же частая проблема. У вас была корутина my_func
(или другое название), к которой вы не прописали await
. Скорее всего вы это должны сделать, поэтому вернитесь и await-ните вашу корутину! Сделайте await my_func()
ValueError - ошибка при работе со переданными значениями.
Возникает, когда вы передаете в функцию значение правильного типа, но неправильного значения. Примеры:
- Переданный символ
":"
(двоеточие) используется функцией для разделения значений в CallbackData, поэтому его нельзя использовать в самих значениях. Это может произойти, когда вы работаете с CallbackData Factory
- Длина callback_data в инлайн кнопках ограничена 64-ю байтами. Бывают такие ошибки:
- Пытаетесь из текста сделать int
AttributeError - ошибка при работе с атрибутами классов.
Такая ошибка возникает, когда вы пытаетесь вызвать атрибут (переменную класса), которую этот класс не имеет.
Примеры:
AttributeError "NoneType" object has no attribute "run_until_complete"
Довольно частая ошибка, когда пытаются сделать dp.loop.run_until_complete
, а dp.loop
оказывается равен None
. Ну и когда этот объект "ничто", у него и не может быть никаких атрибутов. P.S. Никогда не берите loop из объекта dp.
Ошибки при работе с Telegram Bot API
Вообще, частенько, при работе с Бот АПИ, в ошибке указано решение... Но его почему-то не читают.
aiogram.utils.exceptions.TerminatedByOtherGetUpdates
У вас запущено более одного процесса, которые делают запрос getUpdates
к телеграму с одного и того же токена бота Т.е. когда в боте запускается функция start_polling несколько раз в разных процессах.
Вы либо не остановили один процесс и запустили код заново, либо закрыли неправильно Pycharm, не останавливая процесс, и потом запустили бота заново. Либо у вас работает бот на сервере, а вы еще и запускаете на локальном компьютере.
Пожалуйста, найдите этот второй процесс и убейте его. Можно просто перезагрузить компьютер. А если не можете найти - смените токен бота и все решится.
aiogram.utils.exceptions.ChatNotFound
Ошибка возникает, если:
- Вы ввели неверный идентификатор чата (просто неправильное значение передано в аргумент
chat_id
) - Бот не контактировал с пользователем ранее, поэтому он не может ему написать.
aiogram.utils.exceptions.CantParseEntities
В тексте у вас закрался запрещенный символ.
Обычно, если вы используете parse_mode для форматирования текста (HTML или MARKDOWN), то для того, чтобы понять где у вас в тексте жирный шрифт или курсив - используются специальные символы/теги. Примеры:
- Для HTML жирный шрифт будет
<b>жирный</b>
. Поэтому, если в тексте будет что-то типа<coroutine>,
то телеграм его не распознает и выдаст эту ошибку - Для HTML используемые теги надо закрывать, т.е. если вы начали с тегом
<b>
, то где-то его надо закрыть:</b>
- Для MARKDOWN жирный шрифт будет
*жирный*
. Поэтому, если где-то в тексте будет нечётное количество звездочек, т.е. в тексте где-то появилась звездочка, которой вы не хотели текст сделать жирным - будет ошибка.
Ошибку можно решить с помощью функций quote_html или escape_md:
from aiogram.utils.markdown import quote_html text = quote_html("<booo>")
aiogram.utils.exceptions.BadRequest
Название ошибки ни о чем не говорит. Просто то, что запрос не состоялся. А вот решение написано в описании ошибки. Например:
Not enough rights to export chat invite link
- У бота недостаточно прав для того, чтобы создать ссылку-приглашение в чат. Скорее всего он не админ, или просто не имеет на это право.
aiogram.utils.exceptions.ValidationError: Token is invalid!
Все просто, как описано в ошибке. Вы ввели неверный токен. Можете проверить это с помощью функции print. Попробуйте print(BOT_TOKEN)
если у вас эта переменная так называется, и убедитесь сами.
Как Частенько такое вижу, когда мои студенты не переименовывают файл .env.dist в .env.
АЛЛО! Расширение dist используется для примеров файлов, а не для того, чтобы туда писались переменные! Переименуйте и тогда подтянется уже ваш токен из файла .env.
aiogram.utils.exceptions.RetryAfter
В телеграме есть лимиты. Например, сообщения советуют слать не чаще 1 секунды в один и тот же чат. Делайте иногда задержки в своем коде между отправкой нескольких сообщений:
await asyncio.sleep(1)
Прочие ошибки
Тут довольно специфичные ошибки, на случай, если вы не нашли выше описание нужной.
ERROR: Command errored out with exit status 1:...
Если у вас именно такая ошибка, или похожая как на скрине - у вас не установилась библиотека на Python3.9. Эта версия на Windows еще не отлажена окончательно, поэтому решение тут - удалить Python3.9 и поставить Python3.8
asyncpg.exceptions.InvalidCatalogNameError: database "..." does not exist
Тут библиотека asyncpg сообщает вам, что в вашей базе данных отсутствует база с именем, указанным в кавычках.
sqlite3.OperationalError: near ")": syntax error
Синтаксическая ошибка в вашем SQL-запросе. Проследите строчку, где этот запрос делается и проверьте где вы написали что-то несусветное рядом со скобкой. Либо не рядом со скобкой (в ошибке будет написано в кавычках).