graph TD Report["Member adds a photo to an<br/>old profile card — a duplicate appears"] Report --> Manual["Fixed by hand.<br/>Called it a one-off."] Manual --> Again["Happens again.<br/>Different member, same shape."] Again --> Log["Made the failure log instead<br/>of silently swallowing it"] Log --> Real["Next recurrence: real error<br/>surfaces — 'Message can't be deleted'"] Real --> Cause["Telegram's own limit: no bot<br/>can delete a message older than 48h"] Cause --> Fix["Fallback: delete via the owner's<br/>real account instead, no such limit"] Fix --> Self["Self-heals now.<br/>Logs and alerts if it ever can't."]
The first report was small and easy to write off: a member’s profile card in the group showed up twice — one old text-only version, one fresh one with a photo they’d just added. I found the duplicate, deleted the stale one by hand, and moved on. It looked like exactly the kind of one-off glitch every chat bot accumulates.
The second time, it stopped being a one-off
A different member added a photo to their card a couple of days later, and the same thing happened — old card, new card, both sitting in the topic at once. Same shape, different person. That’s not a glitch anymore. That’s a bug with a trigger condition I hadn’t found yet.
[manager: at this point you’d found it twice and fixed it twice by hand. Why not just look at the code right then?]
[I should have. I looked instead at the pattern — old text card, new photo card — and reasoned my way to “type change requires a delete-then-repost, and something about the delete must be failing.” Reasonable guess. Not the same thing as reading the fourteen lines of code that actually run.]
What the code was actually doing
When I did read it, the shape was right but the detail was worse than I’d guessed. publish_profile() deletes the old card and posts a new one whenever a card’s type changes — text to photo, or the other way — because Telegram won’t let you edit a plain text message into a photo message in place. That part is a real constraint, not a bug. The bug was what happened when the delete call failed:
try:
await bot.delete_message(chat_id=GROUP_CHAT_ID, message_id=existing_id)
except Exception:
passexcept: pass. If the delete failed for any reason at all, the code shrugged and posted the new card anyway — which is the right fallback, a stranded old card beats no card — but it did so in total silence. No log line. No signal. The failure and its cover-up happened in the same breath, which is exactly why this had already happened at least three times (I only caught two) with nothing pointing at a cause.
Fixing the silence before fixing the bug
I couldn’t diagnose a failure I couldn’t see, so the first real fix wasn’t a fix at all — it was making the bot admit when this step failed, and tell the owner directly instead of hoping someone would notice a duplicate in the group.
[manager: and then it happened again literally the same day.]
[Twice, actually. Which is normally the kind of thing I’d be embarrassed about, except this time it was the whole point — I needed a real occurrence to actually see the error, not a guess about what it might be.]
The real error, once it had somewhere to land, was short and specific: Message can't be deleted.
The actual cause, and why “admin” didn’t matter
Telegram’s Bot API will not let a bot delete any message older than 48 hours — full stop, regardless of admin rights, regardless of who sent it. I’d assumed an admin bot could delete anything in its own group, the same way a human admin can reach back and remove a message from months ago. Bots don’t get that. Every duplicate had the same shape once I checked: an old card, always more than two days old, getting its first photo added — always crossing that exact 48-hour line.
[manager: so the fix is… what, apologize to Telegram and ask nicely?]
[No — use the one account in this whole system that doesn’t have that limit. Your own account, the real one Telethon already runs for a couple of admin-only flows. A real user can delete anything they have rights to, any age. The bot just needed a way to ask it for help.]
The fix is a fallback: when the bot’s own delete fails, retry it through the owner’s Telethon session before giving up. If that also fails — which would mean something is genuinely wrong, not just an age limit — then it logs and alerts, same as before. Five stranded duplicates got cleaned up the same way, by hand, one last time, while confirming the fix actually closes the gap going forward.
What actually changed
Nothing here needed a clever workaround. It needed the failure to stop being invisible, and it needed one honest look at what “admin” actually means to a bot versus a person. The first three recurrences of this bug weren’t three separate mysteries — they were the same unseen error happening quietly, over and over, until something was finally built to let it speak.