Главная · Блог · PEER_ID_INVALID и «бот не может написать первым»: причины и обход
PEER_ID_INVALID и «бот не может написать первым»: причины и обход
Ошибка PEER_ID_INVALID появляется в двух ситуациях: бот отправляет сообщение по неверному идентификатору или пытается написать человеку, который его никогда не открывал. Второй случай — не сбой, а правило Telegram: бот не может начать диалог первым. Из него следуют вещи, которые ломают планы многих: нельзя загрузить список телефонов и разослать, нельзя написать подписчикам канала, которые не заходили в бота, нельзя «догнать» клиента из CRM сообщением. Ниже — что означает каждая ошибка Bot API, чем PEER_ID_INVALID отличается от блокировки, как строить базу, которой можно писать, и как это отражается в статистике рассылок ЧБТ.
Короткий ответ
PEER_ID_INVALID — ответ Telegram на попытку отправить сообщение «в никуда». Telegram не находит собеседника, к которому у бота есть доступ: либо идентификатор чата неверный, либо этот человек ни разу не писал боту и не нажимал «Начать». Для бота такой пользователь не существует — даже если у вас есть его номер телефона, username и он подписан на ваш канал.
Исправляется это не на стороне бота и не в конструкторе. Единственный способ получить право писать человеку — чтобы он сам открыл диалог с ботом: нажал «Начать», отправил /start или любое сообщение, перешёл по диплинку. С этого момента бот может писать ему без ограничений по времени — до тех пор, пока человек не заблокирует бота.
Если вы видите эту ошибку в статистике рассылки или в логах интеграции — дальше разберём, какая именно причина у вас и что с ней делать. Если ошибка возникает при обычной переписке с ботом, который раньше отвечал, — это другая история, см. чеклист «бот в Telegram молчит».
Словарь ошибок Telegram Bot API про доставку
Telegram отвечает разными текстами на похожие ситуации. Их путают, а лечатся они по-разному.
| Ответ Telegram | Что произошло | Что делать |
|---|---|---|
400 Bad Request: PEER_ID_INVALID | идентификатор не распознан как чат, доступный боту: неверный ID, username вместо ID, человек никогда не писал боту | проверить ID; получить /start от человека |
403 Forbidden: bot can't initiate conversation with a user | ID верный, но человек не открывал бота | только /start со стороны человека |
400 Bad Request: chat not found | чата с таким ID нет или бот его никогда не видел | проверить ID, для каналов — префикс -100 |
403 Forbidden: bot was blocked by the user | человек писал боту, а потом нажал «Заблокировать» | снять с рассылок; писать нельзя, пока не разблокирует |
403 Forbidden: user is deactivated | аккаунт удалён | удалить контакт из базы |
403 Forbidden: bot can't send messages to bots | попытка написать другому боту | боты друг другу не пишут — никак |
400 Bad Request: group chat was upgraded to a supergroup chat | группа стала супергруппой и сменила ID | взять новый ID из поля migrate_to_chat_id |
429 Too Many Requests: retry after N | превышен лимит отправки | подождать N секунд, снизить темп |
Первые две строки — про одно и то же правило «сначала пишет человек», просто Telegram формулирует его по-разному в зависимости от того, как передан идентификатор. Третья — чаще всего опечатка или неверный формат. Четвёртая и пятая — состояние базы, которое меняется со временем. Последние две — технические, к правилу не относятся.
Правило «сначала пишет человек» и почему оно не обходится
Бот в Telegram может отправить сообщение только тому, кто хотя бы раз написал ему сам. Это защита от спама на уровне платформы: иначе любой бот мог бы рассылать сообщения по перебору идентификаторов, и мессенджер превратился бы в почту 2005 года.
Правило не зависит от инструмента. Ни один конструктор, ни собственный код на Bot API, ни платные сервисы его не обходят — если кто-то обещает «рассылку по базе номеров в Telegram», речь либо о личных аккаунтах и серых схемах с баном, либо о СМС. Бот ЧБТ подчиняется правилу так же, как любой другой.
Что считается «написал первым»:
- нажал кнопку «Начать» в чате с ботом — это отправка команды /start;
- перешёл по диплинку
https://t.me/имя_бота?start=payloadи нажал «Начать»; - отправил боту любое сообщение текстом.
Что не считается: подписка на ваш канал, где бот администратор; членство в группе, где сидит бот (в группе бот может отвечать, но в личку этому человеку — нет); номер телефона в вашей CRM; переписка с вашим личным аккаунтом. Тот же принцип действует и в MAX: бот получает событие bot_started, когда человек открывает его, и только после этого может отправлять служебные сообщения — подробно об этом в статье про уведомления клиентам через MAX-бота.
PEER_ID_INVALID, блокировка и «chat not found»: чем различаются для вашей базы
Три ошибки — три разных состояния контакта, и с точки зрения базы они не равны.
Никогда не писал (PEER_ID_INVALID, can't initiate conversation). Этого человека в базе бота нет. Он может быть в вашей CRM, в списке подписчиков канала, в таблице с выставки — но для бота он пустое место. Его нельзя «добавить в базу» вручную: только он сам может это сделать. Стратегия — дать ему повод открыть бота, см. следующий раздел.
Заблокировал (bot was blocked by the user). Человек был в базе, получал сообщения и нажал «Заблокировать бота» — чаще всего после рассылки, которую счёл лишней. Контакт в ЧБТ остаётся со всей историей и тегами, но доставка невозможна, пока он сам не разблокирует. Стратегия — исключить из рассылок и не пытаться достучаться: каждая попытка — ошибка в статистике и лишняя нагрузка на очередь.
Неверный ID (chat not found). Технический сбой на вашей стороне, если вы отправляете через собственный код или интеграцию. Человек, возможно, есть в базе, но вы стучитесь не в ту дверь. Стратегия — искать ошибку в данных, а не в клиенте.
Практическое следствие для владельца бота на конструкторе: раз ЧБТ отправляет сообщения только контактам, которые сами писали боту, PEER_ID_INVALID и chat not found в рассылках почти не встречаются. Ошибки доставки там — это блокировки и удалённые аккаунты, то есть естественное старение базы.
Откуда берётся неверный ID, если вы пишете свой код
Раздел для тех, кто отправляет сообщения через Bot API сам или подключил бота к внешней системе через webhook. Типичные источники PEER_ID_INVALID и chat not found:
- Username вместо chat_id. Отправить сообщение пользователю по
@usernameнельзя — только по числовому ID. По username работают только публичные каналы и группы, и то при условии, что бот в них состоит. - Телефон вместо ID. Bot API не знает номеров телефонов. Номер, полученный через кнопку «Поделиться контактом», — это данные, а не адрес доставки.
- ID из другой библиотеки. Клиентские библиотеки MTProto (Telethon, Pyrogram) работают с парой «ID + access hash» и выдают
PeerIdInvalid, если сессия этого пользователя ещё «не видела». В Bot API access hash не нужен, но ID пользователя, взятый из чужой сессии, ничего не даёт, если человек не писал вашему боту. - ID канала без префикса. Идентификаторы каналов и супергрупп в Bot API начинаются с
-100. ID вида1234567890из ссылкиt.me/c/1234567890/…нужно превратить в-1001234567890. - Группа стала супергруппой. После апгрейда ID меняется. Telegram присылает
migrate_to_chat_id— сохраните новый и больше не используйте старый. - Обрезанный ID. Идентификаторы пользователей давно превысили 2³¹. Поле типа int32 в базе или таблице, которая «округляет» длинные числа, портит ID при сохранении. Храните как int64 или строку.
Правило одно: единственный надёжный chat_id — тот, что пришёл в апдейте от самого пользователя (message.chat.id). Всё, что получено другим путём, — повод для ошибки.
Как строить базу, которой можно писать
Раз бот не пишет первым, задача владельца — сделать так, чтобы первым написал клиент, причём в нужный момент и с нужным контекстом. Инструмент — диплинк: ссылка на бота с payload, по которой в ЧБТ срабатывает триггер «Диплинк» и сценарий начинается с нужного места.
- Сайт. Кнопка «Написать в Telegram» ведёт на диплинк, а не на личный аккаунт менеджера. Виджет ЧБТ на сайте — второй вход: контакт из виджета попадает в ту же базу.
- Канал. Ссылка на бота в описании, в закрепе и в каждом посте, где есть материал или предложение. Подписчик канала ≠ контакт бота, пока он не перешёл.
- Офлайн. Раздел «QR-кампании» в ЧБТ генерирует QR-код со ссылкой-диплинком — на вкладыш в посылку, стойку, визитку, чек. Шаблон «Бот покупателя» построен именно на этом: покупатель сканирует, открывает бота, становится контактом с тегом кампании.
- Момент заказа. Если хотите присылать статусы заказа или напоминания о записи, попросите клиента открыть бота в момент оформления: «нажмите, чтобы получать статус в Telegram». Шаблон «Уведомления» собирает контакт ровно под эту задачу.
- Существующая база телефонов. Превратить её в получателей сообщений нельзя ни в одном конструкторе — импорт заведёт карточки, но не диалоги. Рабочий путь — СМС или письмо с диплинком и внятной причиной перейти: скидка, чеклист, статус заказа. Конверсия таких переходов невысока, поэтому лучше начать собирать базу в боте сегодня, чем догонять её потом.
Отдельно — согласие. Шаблон «Согласие на рассылку» не просто собирает контакт, а получает от человека явное «да, присылайте» и ставит тег, по которому потом отбирается аудитория. Это одновременно и требование 152-ФЗ, и защита от блокировок: тот, кто согласился, реже нажимает «Заблокировать».
Что показывает статистика рассылки в ЧБТ
Раздел «Рассылки» (Базовый тариф и выше) отправляет сообщения через очередь по одному, с паузой 50 мс — не быстрее 20 в секунду, это ниже лимита Telegram на массовую отправку (около 30 сообщений в секунду). Аудитория выбирается по тегам из контактов, которые уже писали боту, поэтому «написать первым» рассылка даже не пытается.
После отправки в карточке рассылки два числа: отправлено и ошибки. Ошибки — это почти всегда bot was blocked by the user и user is deactivated: люди, которые ушли из базы после последнего контакта с вами. Как читать это число:
- Небольшая и стабильная от рассылки к рассылке доля ошибок — норма, естественное старение базы.
- Скачок по сравнению с прошлыми рассылками — база лежала без контакта несколько месяцев, либо предыдущая рассылка была лишней.
- Ошибок много, и с каждой рассылкой их больше — либо базу собирали «холодным» способом (люди открыли бота ради разового файла и не ждали сообщений), либо частота рассылок слишком высока.
Что делать с ошибками: не повторять отправку тем же контактам — блокировка не снимается повтором. Контакты остаются в разделе «Контакты» с историей; имеет смысл вести отдельный тег для тех, кто заблокировал, и исключать его из аудитории следующих рассылок. Про то, как возвращать «спящую» базу так, чтобы не получить волну блокировок, — в статье про реактивацию базы через бота.
Если сообщение нужно отправить из внешней системы
Частый сценарий: заказ изменил статус в вашей учётной системе — надо уведомить клиента в Telegram. Через собственный код это приведёт к PEER_ID_INVALID у всех, кто не писал боту. Но и для тех, кто писал, в ЧБТ нужно понимать, что именно умеет каждый инструмент.
Триггер «Входящий webhook» — это запуск сценария извне: ваша система отправляет POST с секретом на адрес ЧБТ, и сценарий стартует как отдельное событие. Он подходит для служебных задач — «оплата прошла» → действие «Сделка в CRM» и «Уведомить оператора». Чего он не делает: не находит конкретного клиента в Telegram и не пишет ему в его диалог. Сценарий по входящему webhook запускается как новый диалог в канале виджета, а не как сообщение Иванову, который вчера открыл бота. Схема «система узнала об отгрузке → клиент получил сообщение в Telegram» через этот триггер не собирается, и обещать её клиентам не стоит.
Адресные уведомления в ЧБТ доставляются тремя способами, и во всех трёх правило «сначала пишет человек» соблюдено само собой:
- клиент спрашивает сам — триггер «Ключевое слово» или кнопка «Где мой заказ», затем «Сбор данных» с номером заказа и действие «HTTP-запрос» (ответ вашего сервера подставляется в сообщение через поле «в переменную»); статус заказа с Ozon и Wildberries по номеру ЧБТ не получает, такие вопросы уходят оператору;
- рассылка по тегу — для событий, которые касаются группы, а не одного человека;
- оператор из «Диалогов» — для единичных случаев: диалог уже есть, значит, сообщение дойдёт.
Обратное направление — «HTTP-запрос» из сценария — отправляет данные контакта и переменные в вашу систему. Формат входящего и исходящего запросов описан в справке по webhook, а подробный разбор той же схемы для MAX — в статье про уведомления клиентам через бота.
Про импорт. В «Контактах» есть «Импорт CSV», но он не даёт права писать: импортированный контакт, у которого нет диалога с ботом в Telegram или MAX, для платформы недостижим — рассылка по нему уйдёт в ошибки. Импорт полезен, чтобы подтянуть имена, телефоны и теги к людям, которые уже открыли бота, а не чтобы «загрузить базу».
Чеклист диагностики
- Где возникла ошибка? В статистике рассылки ЧБТ — это блокировки, см. раздел про статистику. В логах своего кода — идите по таблице ошибок выше.
- Писал ли человек боту? Проверьте в разделе «Контакты»: если карточки нет — он не открывал бота, и никакой ID не поможет.
- Верный ли ID? Число, не username и не телефон; для каналов — с
-100; хранится без потери разрядов. - Не заблокировал ли? Ответ
bot was blockedоднозначен. Снять с рассылок и ждать. - Не лимит ли?
429— темп отправки. В ЧБТ очередь троттлит сама; в своём коде уважайтеretry_after.
Главный вывод: PEER_ID_INVALID — это не поломка, а напоминание, что база бота строится только руками клиентов. Ставьте ссылку на бота везде, где вас находят, собирайте согласие, следите за долей ошибок — и правило перестанет быть проблемой. Бот с диплинками, QR-кампаниями и согласием на рассылку собирается в ЧБТ на Бесплатном тарифе; рассылки — на Базовом за 650 ₽ в месяц. Подключение канала — на странице конструктора ботов Telegram. Создать бота и начать собирать базу →
Конструктор ботов TelegramЧеклист: бот в Telegram молчитШаблон «Согласие на рассылку»Уведомления через MAX-бота