Пользователь нажимает кнопку под сообщением бота, на кнопке появляется индикатор ожидания и остаётся там навсегда. Задача при этом иногда всё же создаётся, иногда нет. За этим стоят три разных сбоя, и лечатся они по-разному, поэтому первым делом их надо развести.
Три разных сбоя
- Нажатие вообще не дошло до вашего приложения.
- Приложение его получило, выполнило работу, но не подтвердило нажатие.
- Приложение подтвердило слишком поздно, когда идентификатор уже недействителен.
Симптом у всех трёх один — крутящийся индикатор. Дальше разберём, как отличить.
Подтверждение нажатия обязательно
При нажатии inline-кнопки Telegram присылает объект callback_query. Клиент показывает индикатор ожидания до тех пор, пока бот не вызовет answerCallbackQuery. Вызов нужен всегда, даже когда показывать пользователю нечего:
curl -X POST "https://api.telegram.org/bot<ТОКЕН>/answerCallbackQuery" \
-H "Content-Type: application/json" \
-d '{"callback_query_id":"<ID_ИЗ_ОБНОВЛЕНИЯ>"}'
Частая ошибка в коде выглядит так: обработчик сразу идёт создавать задачу в Bitrix24, ждёт ответа портала, потом редактирует сообщение и только в конце подтверждает нажатие. Пока портал отвечает, индикатор крутится. Если портал ответил медленно, подтверждение уже опоздало.
Правильный порядок другой:
получить callback_query
→ проверить пользователя и содержимое callback_data
→ сразу answerCallbackQuery
→ выполнить долгую операцию
→ отредактировать сообщение или прислать результат
Подтверждение снимает индикатор и при необходимости показывает всплывающее уведомление. Саму работу оно не выполняет и сообщение не меняет — это отдельные вызовы.
Про таймаут: чего писать не стоит
В интернете гуляют точные числа вроде «десять секунд» и «тридцать секунд». В публичной документации Telegram срок жизни callback_query_id не указан, поэтому опираться на конкретную цифру не стоит. Проверяемый факт другой: на просроченный или неверный идентификатор сервер отвечает
Bad Request: query is too old and response timeout expired
or query ID is invalid
Практический вывод от этого не меняется: подтверждать нажатие надо немедленно, до обращения к порталу, базе или любому внешнему сервису.
Проверка доставки обновлений
Если в логах приложения нажатия не видно вовсе, дело не в подтверждении. Смотрите, как настроена доставка:
curl "https://api.telegram.org/bot<ТОКЕН>/getWebhookInfo"
Что смотреть в ответе:
url— установлен ли webhook. Пустая строка означает режим опроса;pending_update_count— копятся ли необработанные обновления;last_error_dateиlast_error_message— почему Telegram не смог доставить обновление;allowed_updates— есть ли в спискеcallback_query.
Последний пункт даёт отдельную ловушку. Если при настройке webhook или опроса не передать allowed_updates, Telegram сохраняет предыдущее значение. Однажды заданный список без callback_query продолжает действовать, и нажатия не приходят, пока список не переустановят явно.
Минимальный лог входящего события, по которому потом реально что-то понять:
update_id
callback_query.id
callback_query.from.id
callback_query.data
callback_query.message.chat.id
callback_query.message.message_id Ошибка 409 Conflict: бот запущен дважды
Отдельный случай, который выглядит как случайная работа кнопок: часть нажатий срабатывает, часть теряется. Обычно это два экземпляра бота на одном токене.
Для режима опроса два параллельных getUpdates конфликтуют, и Telegram прерывает предыдущий запрос:
Conflict: terminated by other getUpdates request;
make sure that only one bot instance is running
Webhook и опрос тоже взаимоисключающие: при установленном webhook опрос вернёт ту же 409.
Что с этим делать:
- определить текущий режим через
getWebhookInfo; - для опроса оставить ровно один процесс-получатель;
- при установленном webhook не запускать
getUpdatesвообще; - не использовать
getUpdatesкак проверку живости работающего бота — такой вызов сам создаст конфликт и заберёт часть обновлений себе.
Несколько реплик приложения за webhook при этом допустимы: конфликт возникает именно между несколькими опрашивающими на одном токене.
Что ещё ломает кнопки
callback_dataограничен 64 байтами, и считаются именно байты. Кириллица в UTF-8 занимает два байта на символ, поэтому строка из тридцати русских букв уже за пределом.- Содержимое
callback_dataприходит от клиента, и доверять ему нельзя: проверяйте, тот ли пользователь нажал и разрешено ли ему это действие. - Повторное нажатие на ту же кнопку должно обрабатываться идемпотентно, иначе одна задача создаётся дважды.
- У сообщений, отправленных через inline-режим, обычного
messageможет не быть — вместо него приходитinline_message_id, и редактирование делается по нему.
Порядок диагностики
- Видно ли нажатие в логах приложения? Если нет — переходите к
getWebhookInfo. - Есть ли
callback_queryвallowed_updates? - Растёт ли
pending_update_countи что вlast_error_message? - Не встречается ли в логах 409 Conflict?
- Вызывается ли
answerCallbackQueryи до долгой операции ли? - Не приходит ли в ответ «query is too old»? Значит, подтверждение опаздывает.
Если чинить некогда
Разбор чужой интеграции по логам занимает у нас обычно час-полтора. Пришлите в Telegram ответ getWebhookInfo с замазанным токеном и кусок лога вокруг нажатия — этого хватает, чтобы назвать причину. Настройка и сопровождение интеграций — услуга интеграций.
Смежное: если бот вообще не реагирует на сообщения в группе, причина обычно в правах — разбор в статье бот не видит сообщения в чате. Что умеют кнопки в нашем боте задач — управление задачей кнопками. Сам продукт — бот для Bitrix24, полное руководство — здесь.


