Если callback-кнопки Telegram-бота не работают, сначала определите, доходит ли update типа callback_query до приложения. Причина может находиться в webhook, конфликте с polling, allowed_updates, фильтре обработчика, формате callback_data или коде, который выполняется после нажатия.
Вращающийся индикатор на кнопке не означает, что бизнес-операция выполнилась. Бот должен отдельно подтвердить callback через Bot API и отдельно обработать действие: проверить пользователя, изменить данные и при необходимости обновить сообщение.
Сначала уточните тип кнопки
Callback приходит только от inline-кнопки с полем callback_data. Обычная ReplyKeyboard отправляет сообщение с текстом, URL-кнопка открывает ссылку, а Web App использует другой механизм обмена. Обработчик callback_query не увидит нажатие кнопки другого типа.
- InlineKeyboardButton с callback_data создает callback_query.
- ReplyKeyboardButton отправляет обычное сообщение пользователя.
- Кнопка с url не вызывает callback у бота.
- Web App, login_url, switch_inline_query и платежные кнопки имеют отдельные события и проверки.
- Одинаковая подпись кнопки не гарантирует одинаковый тип действия.
Как проявляется неисправность
- После нажатия индикатор долго вращается, но сообщение не меняется.
- Новые кнопки работают, а старые сообщения — нет.
- Одна кнопка срабатывает, другая с похожим callback_data игнорируется.
- На тестовом боте все работает, а на production — нет.
- Первое нажатие выполняется, повторное возвращает ошибку.
- Действие выполняется, но Telegram продолжает показывать ожидание.
- После запуска второго экземпляра часть callback обрабатывается случайно.
Проверьте, приходит ли callback_query
Добавьте безопасный структурированный лог на входе обработчика обновлений. Достаточно типа update, update_id, безопасного идентификатора пользователя, chat id и короткого имени действия. Не записывайте токен бота, полные персональные данные и секреты из callback_data.
- Нажмите кнопку один раз и запишите точное время.
- Найдите входящий update в журнале webhook или polling-процесса.
- Проверьте наличие callback_query и его id.
- Сравните callback_data с фильтром зарегистрированного обработчика.
- Проследите переход к бизнес-функции и ответ Bot API.
- Если update отсутствует, диагностируйте доставку до кода, а не сам handler.
Webhook и polling не должны конфликтовать
Telegram доставляет обновления либо на установленный webhook, либо через getUpdates. Если webhook активен, polling не получит события. При нескольких polling-процессах обновления могут забирать разные экземпляры, из-за чего поведение кажется случайным.
- Проверьте фактический URL webhook и последнюю ошибку доставки.
- Убедитесь, что HTTPS-сертификат и маршрут webhook действительны.
- Не запускайте polling поверх активного webhook.
- Для polling оставьте один согласованный consumer либо используйте поддерживаемую архитектуру библиотеки.
- Проверьте, не использует ли staging тот же токен production-бота.
Проверьте allowed_updates
При настройке webhook или polling можно ограничить типы обновлений. Если callback_query отсутствует в allowed_updates, Telegram не будет доставлять нажатия, хотя обычные сообщения продолжат работать.
- Проверьте текущую конфигурацию webhook и код запуска polling.
- Добавьте callback_query в явный список нужных типов.
- Учитывайте сохраненную предыдущую настройку при повторной установке webhook.
- После изменения протестируйте новую кнопку и новое нажатие.
Callback нужно подтвердить быстро
После получения callback_query бот должен вызвать answerCallbackQuery. Это убирает индикатор ожидания и при необходимости показывает короткое уведомление. Подтверждение лучше отправить быстро, а тяжелую операцию выполнить отдельно.
- Не ждите внешний API или большой отчет до подтверждения нажатия.
- Обрабатывайте ошибку answerCallbackQuery отдельно от основной операции.
- Не пытайтесь отвечать на слишком старый callback после долгой очереди.
- Короткое уведомление не заменяет обновление сообщения или сохранение данных.
- Повторное подтверждение одного query id может завершаться ожидаемой ошибкой.
Фильтр обработчика не совпадает с callback_data
После рефакторинга префикс, разделитель или регистр callback_data может измениться, а фильтр останется прежним. Сравнивайте фактические байты и логику парсинга, а не только визуальный текст кнопки.
- Проверьте точное совпадение префикса и регулярного выражения.
- Учитывайте экранирование разделителей и пустые части.
- Не помещайте в callback_data большой JSON: поле имеет ограниченный размер.
- Используйте короткий action и непрямой идентификатор состояния.
- Версионируйте формат, если старые сообщения должны продолжать работать.
Почему старые кнопки перестают работать
Сообщение с inline-клавиатурой может храниться у пользователя долго. После обновления бота его callback_data остается старым. Если новый код удалил прежний handler или изменил идентификаторы, нажатие больше не распознается.
- Поддерживайте обработчик предыдущей версии на период миграции.
- Возвращайте понятное уведомление «кнопка устарела» и создавайте актуальное меню.
- Не переиспользуйте старый action для другого опасного действия.
- Храните срок действия сценария и проверяйте его на сервере.
- При массовом изменении обновите клавиатуры активных сообщений, если их id известны.
Состояние и сессия пользователя
Обработчик может получить callback, но отклонить его из-за потерянного состояния диалога, перезапуска процесса или истекшей записи в Redis. Не храните важный контекст только в памяти одного экземпляра бота.
- Связывайте действие с устойчивым server-side идентификатором.
- Проверяйте существование объекта и его текущий статус.
- Храните состояние в общей базе, если бот запущен в нескольких экземплярах.
- Не доверяйте цене, роли или владельцу, переданным только в callback_data.
- Возвращайте понятный ответ при истекшем сценарии вместо молчаливого выхода.
Права пользователя проверяются заново
Callback_data приходит от клиента и может быть воспроизведен. Наличие кнопки в сообщении не является разрешением выполнить действие. Перед изменением заказа, выдачей файла или административной операцией сервер должен повторно проверить пользователя и объект.
- Сверьте Telegram user id с владельцем или разрешенной ролью.
- Проверьте принадлежность объекта организации и чату.
- Используйте одноразовое подтверждение для чувствительных операций.
- Защитите повторное выполнение идемпотентностью.
- Не выводите подробности чужого объекта в сообщении об отказе.
Редактирование сообщения может завершаться ошибкой
Иногда callback обработан, но пользователь не видит результата из-за ошибки editMessageText или editMessageReplyMarkup. Это отдельный этап, который нужно журналировать.
- Message is not modified означает, что новый контент совпадает с текущим.
- Сообщение могло быть удалено или стать недоступным для редактирования.
- В inline-режиме вместо message используется inline_message_id.
- Бот мог потерять права в группе или канале.
- Разметка, длина текста или callback-клавиатура могут не пройти валидацию Bot API.
Тяжелую работу вынесите из webhook
Webhook должен быстро подтвердить прием update. Если внутри выполняется парсинг, генерация файла или обращение к медленному API, Telegram может повторить доставку, а callback устареет.
- Проверьте подпись или секрет webhook и разберите update.
- Зафиксируйте update_id для дедупликации.
- Подтвердите callback пользователю.
- Создайте идемпотентную фоновую задачу.
- Обновите сообщение после завершения или отправьте отдельный результат.
- Обработайте ошибку и возможность повторного запуска.
Пошаговая диагностика
- Убедитесь, что это inline-кнопка с callback_data.
- Проверьте доставку callback_query в журнале входящих update.
- Сверьте webhook или единственный polling-процесс.
- Проверьте allowed_updates и фактический callback_data.
- Зафиксируйте вход в handler и результат проверки состояния и прав.
- Отправьте answerCallbackQuery до тяжелой операции.
- Отдельно проверьте ответ редактирования сообщения.
- Повторите тест новой и старой кнопкой, одним и двумя быстрыми нажатиями.
Как проверить исправление
- Индикатор на кнопке исчезает быстро.
- Действие выполняется один раз даже при повторной доставке update.
- Пользователь без прав получает безопасный отказ.
- Старая кнопка обрабатывается совместимо или сообщает об устаревании.
- После перезапуска и при нескольких экземплярах состояние не теряется.
- Ошибки Bot API видны в журнале без токена и персональных данных.
- Тяжелая операция не удерживает webhook и сообщает итог пользователю.
Типичные ошибки
- Обрабатывать inline-кнопку как обычное текстовое сообщение.
- Забыть callback_query в allowed_updates.
- Одновременно включить webhook и polling.
- Не вызывать answerCallbackQuery и оставлять индикатор ожидания.
- Хранить весь контекст только внутри callback_data.
- Считать показ кнопки достаточной проверкой прав.
- Игнорировать старые сообщения после изменения формата callback.
- Не разделять ошибку действия и ошибку редактирования сообщения.
Как предотвратить повторение
- Версионируйте схему callback_data и оставляйте совместимый fallback.
- Добавьте дедупликацию update_id и идемпотентность действий.
- Тестируйте кнопки после развертывания на production-подобном боте.
- Мониторьте ошибки webhook, очередь update и время подтверждения callback.
- Храните состояние в надежном общем хранилище.
- Добавьте тесты прав доступа и повторного нажатия.
- Не записывайте токен бота и секретные данные в журналы.
Итог
Если callback-кнопки Telegram не работают, последовательно проверьте тип кнопки, доставку callback_query, webhook или polling, allowed_updates, совпадение callback_data и подтверждение answerCallbackQuery. Затем отдельно диагностируйте состояние, права, бизнес-операцию и редактирование сообщения.
Если кнопки продолжают зависать, я могу проверить доставку update, обработчики Telegram-библиотеки, webhook, состояние и ошибки Bot API, затем исправить сценарий и добавить защиту от повторных операций.