Key takeaways
- On August 3, the first day our queued social posts were both approved and due, seven of them went out three times each.
- Posting to several accounts in one call returns a different kind of id than posting to one. Our script only knew the single account shape, so every successful post read as a failure.
- The script retried each "failure," and every retry created another live post.
- We fixed that script. Two hand copied sibling scripts had the identical line and kept tripling posts while the incident read as handled. All three were patched the next day.
- The rules now: a create that came back successful happened, an unreadable id is recorded as unknown and never retried, and a fix to one script means searching every copy for the same line.
A big chunk of the work we do as a GHL consultant in the Bay Area is scheduling. Posts, texts, emails, reminders, all lined up to go out at the right time without anyone pressing a button. We run our own social channels on the same kind of machine we build for clients.
On August 3 that machine posted the same thing three times. Seven times over.
The first day it was real
Our social queue works like this. Every post is built ahead of time, gets approved, and sits in a list with a time slot. Every few minutes a small script checks the list, finds anything that is approved and due, and sends it to the posting platform. The platform publishes it to Facebook, Instagram, LinkedIn and the rest in one go.
August 3 was the first day anything in that queue was both approved and due. So it was the first day the sending part had ever run for real. When we went back through the logs later, there was not a single successful post in any run before it. Nothing had broken. That path had just never been tested.
Seven slots came due that day. Each one went out, and then went out again, and then a third time. We saw the triples on our own feeds.
The receipt it could not read
When the platform accepts a post, it sends back a receipt with an id on it. Our script needed that id to mark the post as done.
The format depends on the call. Post to one account and the id comes back in one format. Post to several accounts in a single call and it comes back as a completely different format. Every post in our queue went to several accounts at once, so every receipt came back in the format our script had never been told about.
So the script looked at a perfectly good receipt, couldn't find an id it recognised, and decided the post had failed. Then it did what scripts are supposed to do with a failure. It tried again.
The post hadn't failed. It was live. The retry made a second live copy, and that receipt couldn't be read either, so it tried a third time. Three attempts, three real posts, all logged as failures.
Fixed, and still happening
We found the line, taught it both id formats, and the queue stopped tripling. Incident handled.
Except it wasn't. Two other posting scripts, one for our blog promos and one for another account, had started life as copies of the first one. Both carried the identical line. The promos kept going out three times while our notes said the problem was fixed. All three scripts were patched the next day, August 4.
A bug you copy into three scripts needs fixing three times.
Why there was no undo
The obvious cleanup is to delete the extra copies. Two days earlier we had already learned what that does. Deleting a published post through the scheduling platform reports success and only clears the platform's own record. The post on Facebook or TikTok stays up until someone with access to that account removes it by hand.
That's what makes this kind of retry so expensive. Retrying a step that has no side effect is harmless. Retrying a step that publishes something, sends something, or charges something makes the mistake again every time it runs.
The rules we run on now
First, a create that came back successful is a create that happened. If the platform said yes and we can't read the id, we record the id as unknown and move on. We never retry it. An unknown id is a small bookkeeping problem. A second copy on a customer's feed is a public one.
Second, we know which step is safe to retry. Uploading the image again is fine, because it doesn't post anything. Sending the post again is not. Those used to look the same in our logs, and now they don't.
Third, a fix to one script means a search across all of them. Before we call a posting bug fixed, we search every script that posts for the same pattern.
And we correct a wrong status the moment we know. A post marked failed that actually went out is a loaded gun. The next "retry everything that failed" pass fires another copy.
Later on the platform stopped returning an id at all. Because of August, our scripts were already built to treat that as unknown rather than failed. Now they look the post up right after creating it to find the real id.
What this means for Bay Area businesses working with a GHL consultant
You don't need to write scripts for this to apply to you. Anything in your business that retries on its own can do it. The booking confirmation that goes out twice. The payment form a customer clicks again because the page hung. The follow up sequence that restarts when someone fills the form a second time.
One duplicate looks like a glitch. Three, to a customer, looks like nobody's running the place.
Three things worth doing this week:
- Pick your two most important automations, usually the new lead text and the booking confirmation. Fill in your own form twice in a row and see what you get.
- Ask whoever built your system what happens when a send seems to fail. Does it retry? How does it know the first one really failed?
- If something got fixed recently, ask where else the same setup lives. Copied workflows and duplicated funnels carry the same mistake.
A good GHL consultant in the Bay Area should be able to tell you what every automation does when something goes wrong, not just when it goes right.
Want this built for you
We build automations that know the difference between failed and finished, and you own every piece of them. Start at optechsol.llc.