Trade With Viet
Session Explainer
Internal use only — enter access password
bugfix

TWV Facebook Auto-Publish: The Long-Wait Bug & The Mass-Post Incident

2026-06-23 · explainer.tradewithviet.com

The Facebook auto-publish rail works — the real problem was three posting systems running at once with no coordination.

The reported bug ('scheduled clips never post') was a misread of how n8n waits work. While confirming the fix, a bigger issue surfaced: old schedulers were never turned off, so they collided with the new rail and produced a burst of posts including a duplicate. Source stopped, duplicate removed, future dupes auto-guarded.

StatusContainedRailn8n direct-to-FBDate2026-06-23

6-layer thinking chainChuỗi tư duy 6 tầng

L1
Raw requestYêu cầu gốc
"long-wait executions never post — the webhook returns 200 but nothing shows up; fix it."
L2
Reframe — what was really neededDiễn giải lại — nhu cầu thật
The need wasn't 'repair a broken workflow' — it was 'make scheduled Facebook posting reliable and cap-free.' Before changing anything, the real question was: is the workflow actually broken, or are we misreading the evidence?
L3
ConstraintsRàng buộc
Constraints discovered: (1) the n8n API does NOT list a long-wait as a 'waiting' execution — the wait lives in memory and only appears AFTER it fires, so 'zero waiting rows' proves nothing; (2) VPS SSH was blocked (host-key), so live server logs were unavailable; (3) every live FB post is public and irreversible; (4) Gemini and ElevenLabs voice credits were both depleted.
L4
Options weighedCác lựa chọn cân nhắc
Paths weighed: (A) rewrite the workflow assuming it's broken — risky, no evidence it's the cause; (B) SSH the server and read logs — blocked by host-key; (C) fire ONE controlled real clip with the correct timestamp format and just watch the live Facebook page — cheap, definitive, and doubles as a real backlog post; (D) abandon n8n and drive everything from the Mac on short timers — reliable but heavier and leaves the real cause unknown.
L5
Principle invokedNguyên tắc áp dụng
Verify against ground truth before theorising: watch the actual outcome (did it post?), not a proxy metric (an API status field that can lie).
L6
Pick + recognition signalLựa chọn + dấu hiệu nhận biết
Picked (C): fired the jun22 clip with a +07:00 timestamp to a fresh slot ~38 min out. It posted at exactly 17:25 ICT. That single observation proved the rail works and killed the 'workflow is broken' theory. Rejected (A) as premature, (B) as blocked, (D) as unnecessary once (C) proved the rail. Recognition signal: when an API status contradicts what you can observe directly, trust the observation and design a test that produces a visible real-world result.

Why posts came out in a burst: three rails, no shared brain

Publishing was being driven by three independent systems that didn't know about each other. Each one, on its own, behaved correctly. Together, with no shared coordination, they piled posts onto the same evening window.

Old timers4 launchd jobs10-min pollercron jobNew manual firesn8n webhook20:30 windowcollisionFB pageburst + dup
Technical termThuật ngữ kỹ thuậtPlain nameTên dễ hiểuRoleVai trò
launchd agents (clip-drip, jun22-26, w04b-w04c, w05bc)Old Mac timersPre-n8n schedulers that fired backlog clips on their own daily; never switched off.
cron twv-publish-poller.py (*/10)10-minute pollerChecked for approved content every 10 minutes and published it — a second independent publisher.
n8n TWV-VID-FB webhookNew direct railThe cap-free Facebook publisher this project built; the manual fires this session went here.
in-memory Wait nodeParked timer inside n8nHolds a scheduled post in memory until its time; invisible to the API until it fires — the source of the 'zero waiting' confusion.

Read it in 30 seconds: three arrows, one target. The old timers and the poller were still alive, and earlier retry bursts had left timers parked inside n8n. They all landed near 20:30, plus the new manual fires — so the page got a cluster, including one clip posted twice.

Lesson to carry forward: when you add a new publishing rail, the first job is to find and switch off every other rail. One system owns publishing; the rest must be silenced first.

What happened, in order

1
Compare to a working twin
TWV-VID-FB vs BEUP's VID-09 — graph, timestamp format, topology all byte-identical. Not a config bug.
2
Controlled live test
Fired jun22 clip, +07:00 format, fresh slot, ~38 min out. It posted at exactly 17:25 ICT. Rail works.
3
Drain backlog to fresh slots
jun22 posted; w04b + w04c scheduled one per day at fresh times.
4
Burst detected
20:48 verification check found a 20:30-20:31 cluster with an exact duplicate — three rails colliding.
5
Stop the source
Disabled the 4 old launchd posters + the 10-min cron poller (reversible).
6
Clean + guard
Deleted the live duplicate; scheduled a dupe-guard for Jun24 & Jun25 20:43 to auto-remove the 2 remaining parked dupes.
7
Content fix
Re-rendered the negative-framed 'Power Cuts' clip into 'Lock reliable Q3 capacity'; scheduled Jun25.

Layered decision cardsCác quyết định theo tầng

Trust the live page, not the API status field
L1The n8n API showed zero 'waiting' executions, which looked like proof the schedule was broken.
L2The real question: does 'no waiting row' actually mean 'nothing scheduled', or does the API just not show in-memory waits?
L3n8n holds a long wait in memory and only writes a database row when it fires; SSH to read logs was blocked.
L4(a) Believe the API and rebuild; (b) find another proxy metric; (c) fire one real clip and watch the public page.
L5Ground truth beats proxy metrics.
L6Fired one clip; it posted on time. Rejected rebuilding (no evidence) and hunting more proxies (slower). Recognition signal: an API status that contradicts observable reality is a measurement gap, not a fault.
Stop the source before cleaning the symptoms
L1Posts were appearing in a burst on the Facebook page.
L2Deleting posts treats symptoms; the burst would keep regenerating unless the publishers were silenced.
L3Four launchd timers + a 10-minute cron poller were still live and posting; the n8n rail also had old parked timers.
L4(a) Delete posts as they appear; (b) disable all old auto-posters first, then clean; (c) restart n8n to flush memory.
L5Fix the cause before the symptom; never restart something that also kills the good work in flight.
L6Disabled the old timers + poller (reversible), then deleted the duplicate. Rejected restart-n8n (would also wipe the two legitimate scheduled posts). Recognition signal: when output is duplicating, find every producer first.
Guard the future dupes instead of a risky restart
L1Two old timers are still parked in n8n memory and will re-post w04b (Jun24 20:30) and w04c (Jun25 20:30).
L2These can't be cancelled through the API; the only kill switch is an n8n restart, which also deletes the two posts we WANT.
L3The unwanted dupes fire at 20:30; the legitimate posts fire at 20:45 — a clean 15-minute separation.
L4(a) Restart n8n and re-fire everything; (b) delete dupes by hand each night; (c) a tiny scheduled guard that deletes only the 20:25-20:42 window.
L5Prefer the narrow, automatic, reversible fix over the broad destructive one.
L6Scheduled a dupe-guard cron for Jun24 & Jun25 at 20:43 that deletes only the 20:25-20:42 window, leaving the 20:45 posts untouched. Recognition signal: when you can't cancel a timer, fence the outcome with a time-boxed cleanup.

Artifact mapBản đồ tệp tạo ra

PathĐường dẫnWhatLà gìWho reads itAi dùng
scripts/twv-automation/wp_twv.pyAdded normalize_due (forces +07:00), fire_n8n, schedule_n8nReusable, format-correct n8n caller
scripts/twv-automation/twv-n8n-distribute.pyNew CLI to fire a clip to the n8n rail by id + due timeOperator tool for cap-free posting
scripts/twv-automation/twv-dupe-guard.pyDeletes FB videos inside a given ICT time window on a dateAuto-cleanup of the 2 parked dupes
crontab (Jun24 & Jun25 20:43)Two one-shot dupe-guard runsRemoves w04b/w04c dupes when old timers fire
twv-clip/jun23-power-cuts/beatsheet.en.jsonReframed to 'Lock reliable Q3 capacity' + skyline coverPro-Vietnam framing fix; re-rendered + scheduled Jun25
DISABLED launchd + cronclip-drip, jun22-26, w04b-w04c, w05bc + 10-min pollerStopped the mass-post source (reversible)
Pending manual actions — required before fully liveViệc cần làm thủ công — trước khi hoàn tất
  • Top up Gemini TTS and/or ElevenLabs credits — both are at zero, so clip voiceovers currently fall back to the free Edge voice (good, not premium).
  • Watch the Facebook page on Jun24 and Jun25 around 20:30-20:45: the dupe-guard should delete the 20:30 re-post and leave the 20:45 post. Log: scripts/twv-automation/content/dupe-guard.log.
  • Decide the long-term owner of publishing: keep the old launchd/cron timers OFF and let the n8n rail own it, or formally re-enable a single scheduler. They are currently disabled (reversible via launchctl load -w and the crontab backup at /tmp/twv-cron.bak).
  • If you want the parked dupes gone for certain rather than guarded, an n8n restart on the VPS flushes them — but it also clears the two legitimate scheduled posts, which would then need re-firing.

Check your understanding

Why did 'zero waiting executions' in the n8n API NOT prove the schedule was broken?
What was the actual root cause of the burst of posts?
In your own words: why was a time-boxed dupe-guard chosen over restarting n8n to remove the two parked dupes?
Mastery checklist — tick what you can explain unpromptedBảng tự đánh giá — tích những gì bạn tự giải thích được