Posted means confirmed: how publish once works
The worst thing a scheduler can do is post the same thing twice, or say it posted when it did not. Here is how NioConnect tries to avoid both, in plain words, including where the guarantee is weaker than we would like.
The NioConnect team6 min readOn this page
Two ways to get publishing wrong
A publish can go wrong in two opposite directions. The post can be lost, which is the quiet failure described in why scheduled posts fail silently. Or it can be sent twice, which is louder and in some ways worse, because a duplicate has already reached your audience and cannot be taken back.
The two pull against each other. The easy cure for a lost post is to try again, and the easy way to try again is to risk a duplicate. Everything in this article is about trying again safely.
One job per post, and the database decides
Every post has one row in the database, and that row holds its state. The queue that runs the work is only the way a post gets to the platform; it is not where the truth lives. If the queue forgets a job, the row still says what should happen next.
Each post also has exactly one job in the queue, named after the post. Asking for a second job for the same post replaces the first rather than adding to it. A job that is already running is never cut short, because stopping it between the platform call and the moment we record the result is exactly how a published post ends up recorded as unpublished.
The first thing the worker does when it picks up a job is reload the row and check that the post is still due. If it was cancelled, edited, already published or put on hold in the meantime, the worker leaves without sending anything.
A key made before the platform is called
Before the first call to a platform, NioConnect writes down a delivery key for that post and marks the attempt as started. The key is made once. It does not change when the attempt is retried, and it is the same value on the wire the second time as the first.
That ordering matters. If the key were made after the call, a crash in between would leave no record that anything was ever attempted, and the next attempt would have nothing to compare against. Writing it first means a retry always knows that a previous attempt may have reached the platform.
The attempt also carries a lease: a short claim that says this worker is handling the post right now. The lease is what stops two workers from sending the same post at the same moment.
What happens after a crash
Workers do crash, and servers do restart. A reconciler runs about once a minute and looks for three kinds of stuck post: a queued post that is due but has no live job, a publishing post whose lease has gone stale, and a retry that was parked and whose time has come.
It does not act on a stale lease the moment it expires. It waits a grace window first. The reason is uncomfortable but simple: some publishes legitimately take a long time (a large video upload can outlive a short lease), and re-driving an attempt that is still alive would put a second post in front of a real audience. Waiting costs a little time; guessing wrong costs trust.
When the reconciler does act, it does not change the post by hand. It arms a job, and the ordinary claim does the rest, with a fresh fencing token that locks the dead attempt out of the row. The reconciler also asks the same admission question as every other path, so it cannot resurrect work that an emergency stop, an unpaid account or a missing approval has put on hold.
If a post is held, it keeps its place and says why. After a lifted emergency stop, a post whose time passed during the stop is held rather than published hours late.
What each platform lets us promise
Here is the honest part. A delivery key only prevents a duplicate if the platform understands it. Some do; most do not. So every platform adapter declares which of three strategies it uses, and the worker follows that declaration rather than assuming.
| Strategy | Platforms | What a retry does |
|---|---|---|
| Key honoured | Bluesky, Mastodon, Discord | Sends the same key again, so the platform returns the original post |
| Deterministic | Nostr | The note is signed to a fixed identity, so sending it again produces the same note |
| Read back first | X, Telegram, Reddit, Slack, Twitch, Facebook, Instagram, Threads, YouTube, LinkedIn, TikTok, Pinterest | Looks at the account’s own recent posts before sending, and only sends if the post is not there |
For the twelve read back platforms the guarantee is at least once, with a small window in which a duplicate is possible and detectable, made safe by looking before posting again. It is not magic, and we would rather say so than claim an exactly once guarantee a platform cannot give.
Threads resume where they stopped
A thread is a chain of posts, each replying to the one before. If part three of five fails, parts one and two stay published and the thread shows how far it got, not a plain failure.
To resume, NioConnect stores the platform’s own id for every part that landed, because the next part has to reply to a specific earlier post. Retry picks up at the first unpublished part and never re-sends a part that is already out. Without that stored id the only alternatives would be to start a new thread or to guess, and both end with an orphaned reply in a real feed.
Published only after the platform confirms
None of this would matter if the status could lie. A post is marked published only once the platform has confirmed it. Until then it reads as queued or publishing, and if it fails it reads as failed, with one reason from a closed list. The guide to those reasons goes through each one. The reliability page shows the same flow in the product.
What you can do to help
Mostly, nothing, which is the point. But three habits keep you on the safe side.
- Retry once, then read. Retry sends only the channels that failed, so pressing it again does not double post to the ones that worked. If it fails again, the reason has changed or has not been fixed.
- Trust the status over the platform feed. A post can take a moment to show up on a platform after it is accepted. Check the calendar before you resend by hand.
- Choose the fork on purpose. If you save or drag a post that has already published, you are asked whether to correct the record or publish it again. Publishing again makes a new post with its own key, which is why it is allowed to appear twice.
Written by The NioConnect team.
Posts that say why they failed, on one calendar.
Start 7-day trial