Если 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.

  1. Нажмите кнопку один раз и запишите точное время.
  2. Найдите входящий update в журнале webhook или polling-процесса.
  3. Проверьте наличие callback_query и его id.
  4. Сравните callback_data с фильтром зарегистрированного обработчика.
  5. Проследите переход к бизнес-функции и ответ Bot API.
  6. Если 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 устареет.

  1. Проверьте подпись или секрет webhook и разберите update.
  2. Зафиксируйте update_id для дедупликации.
  3. Подтвердите callback пользователю.
  4. Создайте идемпотентную фоновую задачу.
  5. Обновите сообщение после завершения или отправьте отдельный результат.
  6. Обработайте ошибку и возможность повторного запуска.

Пошаговая диагностика

  1. Убедитесь, что это inline-кнопка с callback_data.
  2. Проверьте доставку callback_query в журнале входящих update.
  3. Сверьте webhook или единственный polling-процесс.
  4. Проверьте allowed_updates и фактический callback_data.
  5. Зафиксируйте вход в handler и результат проверки состояния и прав.
  6. Отправьте answerCallbackQuery до тяжелой операции.
  7. Отдельно проверьте ответ редактирования сообщения.
  8. Повторите тест новой и старой кнопкой, одним и двумя быстрыми нажатиями.

Как проверить исправление

  • Индикатор на кнопке исчезает быстро.
  • Действие выполняется один раз даже при повторной доставке 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, затем исправить сценарий и добавить защиту от повторных операций.