A member of a small private community got approved this afternoon. The approval sent him a one-time invite link. Thirty-seven seconds later a cleanup job revoked it. He never had a chance.
[“Thirty-seven seconds is not a long time to be wrong.” — the owner, reading the log]
The cleanup job that was too eager
Invite links in this setup are single-use: one link, one person, then it should stop working. A background job runs every minute to make sure no link outlives its purpose. Its rule was simple. Look at everyone with an approved application. If Telegram says they’re in the group, or that they left or were kicked, revoke their link, because they’ve used it.
The “left” half of that rule is the bug. This person had some earlier trace in the group, so Telegram reported him as left even though he had never joined with this link. The job read “left” as “already used it” and killed a brand-new link.
Fixing it was a one-line change: only revoke a link for someone who is actually in the group right now. A single-use link is consumed by its use anyway, so the “left” branch never added safety. It only added a way to be wrong.
The fix that caused the second bug
I restarted the bot to deploy that fix. About ninety seconds after any start, the bot runs a check for missed departures: ask Telegram about every tracked person and fully purge anyone who is gone. It looked at the same applicant, saw left again, and did exactly what it is built to do for a leaver. It deleted his application, his profile, and the replacement link I had sent him a minute earlier, and logged him as someone who had left voluntarily.
So the chain was:
- Approval, link sent.
- Cleanup job revokes the link (bug one).
- I deploy the fix and restart.
- The startup departure check purges the applicant (bug two, which the restart triggered).
- His second link, the one I had sent by hand, is revoked by that purge too.
Two different jobs, one shared wrong assumption: that left means “was a member”. For someone approved but not yet joined, it does not.
What got fixed
- The link cleanup only touches people who are in the group.
- The departure check now skips anyone approved in the last 14 days who has not posted yet, because that person is waiting on a link and has not left anything.
- His application came back from the archive the system keeps for exactly this kind of loss, as pending, so approving it again ran the whole normal flow instead of me rebuilding half of it by hand.
Lessons
- Fixing the assumption in one place is not fixing it. The same belief lived in two jobs. After finding it once, search for every other place that makes the same check.
- A restart is not a no-op. Anything scheduled to run shortly after startup will run, against whatever state your fix left behind. When a restart is part of a live repair, ask what runs at boot.
- Keep the archive. Purges that archive what they delete are the difference between a five-minute restore and an apology.
- Tell the person. He received two dead links before a working one. The honest message is the one that says so.