Skip to content

Custom emoji fail silently for an ineligible bot — verify by round-trip - #3

Open
hlibsuslov wants to merge 4 commits into
mainfrom
docs/custom-emoji-verified
Open

Custom emoji fail silently for an ineligible bot — verify by round-trip#3
hlibsuslov wants to merge 4 commits into
mainfrom
docs/custom-emoji-verified

Conversation

@hlibsuslov

@hlibsuslov hlibsuslov commented Aug 5, 2026

Copy link
Copy Markdown
Owner

The skill told agents to expect a rejection when a bot may not use custom emoji, and to handle it as a validation error. That is wrong — and it makes every fallback ladder built on it dead code.

Verified against the live Bot API on 2026-08-05, on a bot that qualified for neither route (no Fragment username, owner Premium not active). Telegram returned ok: true and stripped the entities. Reproduced identically through three paths:

Sent as Came back
parse_mode=HTML + <tg-emoji> entities: []
explicit MessageEntity, correct UTF-16 offset entities: []
sendRichMessage + RichTextCustomEmoji in a table cell plain string

No exception is ever raised, so the failure is invisible to any error-driven fallback.

What changed

  • references/custom-emoji.md — the silent-strip failure mode; the read-the-message-back check that is the only reliable probe; why icon_custom_emoji_id cannot be used to detect eligibility (Telegram echoes reply_markup verbatim); and a caution that Premium-only profile fields outlive the subscription and must not be used to infer eligibility.
  • The MTProto rule that an entity must wrap exactly one emoji equal to the sticker's alt, or the server ignores it — same silent failure mode.
  • The image route that works when a bot is ineligible: sendSticker refuses emoji stickers (can't send emoji stickers in messages), but a sticker's thumbnail or file downloads and re-uploads as an ordinary photo. This matters because fallback characters collide — a real 80-emoji pack measured here had only 52 distinct ones, so five different emoji all showed as the same character.
  • Other surfaces recorded: supergroup custom_emoji_sticker_set_name, polls accepting only custom emoji entities, reactions treating bots as non-premium.
  • references/errors-and-fallbacks.md — corrected the "expect a 400" row, added the no-error-at-all failure class.
  • SKILL.md — one non-negotiable rule: prove custom emoji shipped.

Local validators pass.

…und-trip

The skill told agents to expect a rejection when a bot may not use custom
emoji, and to handle it as a validation error. That is wrong, and it makes
every fallback ladder built on it dead code.

Verified against the live Bot API on 2026-08-05, with a bot whose owner has an
active Premium subscription sending to that owner's private chat — squarely
inside eligibility route 2 as documented. Telegram returned ok: true and
stripped the entities. Reproduced identically through parse_mode=HTML
<tg-emoji>, explicit MessageEntity with a correct UTF-16 offset, and
sendRichMessage with RichTextCustomEmoji in a table cell: entities came back
empty and the cell came back as a plain string. No exception is ever raised.

So the reference now teaches the only check that works — read the sent Message
back and look for the entity — and records what does not work: icon_custom_emoji_id
is echoed verbatim regardless, so a button field proves nothing.

Also documented, from the official docs and the same test session: the MTProto
rule that an entity must wrap exactly one emoji equal to the sticker's alt or
the server ignores it; the supergroup custom_emoji_sticker_set_name route;
that polls accept only custom emoji entities; that sendSticker refuses emoji
stickers outright; and the image route that does work — a sticker's thumbnail
or file downloads and re-uploads as an ordinary photo. That matters because
fallback characters collide: a real 80-emoji pack measured here had only 52
distinct ones, so five different emoji all showed as the same character.
Copilot AI lite review requested due to automatic review settings August 5, 2026 21:27
The section claimed the failing bot's owner had active Premium, which would
have made it a Telegram-side gap. It did not: the owner's subscription was
inactive, so this is the documented ineligible case — the finding that matters
is the failure *mode*, not a contradiction of the docs.

Also records the mistake that produced the wrong claim, because it is an easy
one to repeat: Premium-only profile fields such as emoji_status_custom_emoji_id
and background_custom_emoji_id outlive the subscription, so they cannot be used
to infer that a bot is eligible. Only the round-trip can.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the project’s operational guidance around Telegram custom emoji to reflect a verified “silent strip” behavior (Telegram returns ok: true but removes custom emoji entities when the bot is effectively ineligible), and adjusts the error/fallback playbooks accordingly so agents don’t build dead fallback ladders based on expected 400s.

Changes:

  • Adds a non-negotiable SKILL rule requiring proof (via round-trip inspection) that custom emoji entities actually shipped.
  • Corrects the “custom emoji failure” classification in the errors/fallbacks reference by introducing a “no error at all” failure mode and re-scoping the 400 case to malformed payload/IDs.
  • Expands the custom emoji reference with a verified silent-strip section, round-trip verification approach, and practical fallback guidance (including the “show a picture instead” route).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
SKILL.md Adds a hard rule to verify custom emoji delivery by inspecting the returned Message, not by error handling.
references/errors-and-fallbacks.md Updates the failure-mode table to include silent stripping and corrects the 400-row guidance.
references/custom-emoji.md Documents the silent-strip behavior, round-trip detection, and fallback strategies across surfaces.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

hlixli added 2 commits August 5, 2026 21:31
…lly blocks

Agents were reaching for markup fixes and retries when custom emoji did not
appear, because nothing in the skill said loudly enough that the capability is
gated on the *owner's* Telegram Premium. Combined with the silent strip, that
is a debugging trap: there is no error to follow, so the search goes to the one
place the problem never is.

custom-emoji.md now opens with the rule and the instruction to stop: check
whether the account that created the bot has an active Premium subscription (or
a Fragment username), tell the user if it does not, ship the Unicode fallback,
and confirm the fix by round-trip. An expired subscription behaves exactly like
never having had one, so 'they had Premium once' is not an answer.

SKILL.md carries the same as a non-negotiable rule and in the button section.
The section rested on a single negative result, which cannot distinguish
'not entitled' from 'broken somewhere else'. The same bot was re-tested after
Premium was activated on the owner account, with no code change: the entity
survived through all three paths, including inside a rich table cell. Both
halves of the experiment are now stated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants