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

n8n Dedup Guard: 10 Orphaned Claims & How to Clean Them Up

2026-06-21 · explainer.tradewithviet.com

A scan loop fired 10 phantom webhooks — here's how we found and killed every stale claim

Scheduling a print-shop video (bo-cong-cu-xuong-in) revealed a hidden bug: a prior scan loop had called the n8n VID-05 and VID-09 webhooks 5× each with placeholder captions. Each call created a real waiting execution AND locked a Dedup Guard slot claim in workflow staticData. Deleting the executions was not enough — the claims lived on. This explainer walks through the full repair chain and the 5 rules to prevent it from happening again.

StatusResolvedScopen8n VID-05 (IG) + VID-09 (FB)Date2026-06-21Productbo-cong-cu-xuong-in

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

L1
Raw requestYêu cầu gốc
"ok" — user approved applying the cross-platform stagger for bo-cong-cu-xuong-in: YT Jun 30 (keep), IG Jul 3, FB Jul 5.
L2
Reframe — what was really neededDiễn giải lại — nhu cầu thật
The real problem was not just scheduling. The scheduling system was in a corrupted state: a previous scan loop had called VID-05 and VID-09 webhooks 5 times each with placeholder data. Each call produced a waiting execution (invisible in the standard list API) AND wrote a permanent slot claim into workflow staticData. Simply queuing a new video would have collided with the ghost claims — Dedup Guard would have rejected or double-posted.
L3
ConstraintsRàng buộc
• n8n staticData is stored separately from the workflow definition — `n8n_update_partial_workflow` has no operation type for it • Waiting executions (paused at 'Wait Until Due' node) do NOT appear in `n8n_executions` list API with `status: waiting` • Deleting an execution does NOT clear its Dedup Guard slot claim — the claim lives in staticData independently • FB slot Jul 5 21:00 was already taken by the filament-resin tracker — only 08:30 was free • Any webhook call, even with placeholder data, creates a real execution + real slot claim immediately
L4
Options weighedCác lựa chọn cân nhắc
Option A — Deactivate VID-05/VID-09 and re-create them: safest, but destroys all 25+ legitimate claims and would break currently-scheduled posts. Cost: high collateral damage. Option B (CHOSEN) — Surgical repair: delete the 10 orphaned executions individually, then patch both workflow staticDatas via `n8n_update_full_workflow` to remove only the 10 ghost claims, then call VID-05/VID-09 once each for the correct slots. Cost: careful, must not touch legitimate claims. Option C — Let orphaned executions fire and fail naturally: they would post duplicate/blank content on the wrong dates. Cost: live audience sees garbage posts. Option D — Manually cancel via n8n UI without patching staticData: leaves ghost claims in staticData, next scheduling attempt still collides.
L5
Principle invokedNguyên tắc áp dụng
Surgical repair at the lowest broken layer: executions → staticData → new schedule. Fix only what is broken; never destroy legitimate state to reach simplicity.
L6
Pick + recognition signalLựa chọn + dấu hiệu nhận biết
CHOSEN: Delete 10 orphaned waiting executions by ID → patch VID-09 staticData (remove 5 FB ghost claims, keep 25 legitimate) → patch VID-05 staticData (remove 5 IG ghost claims, keep 25 legitimate) → call VID-09 webhook for FB Jul 5 08:30 → call VID-05 webhook for IG Jul 3 20:00 → split calendar-data.json from 1 combined entry into 3 per-platform entries. REJECTED: Deactivate/recreate workflows (Option A) — would have broken scheduled posts for other products. RECOGNITION SIGNAL: Next time a VID-0x webhook needs 'just a quick test call' — STOP. Every call is real. Run `n8n_get_workflow mode=full` first, inspect `staticData.claims`, confirm the target slot is free.

How the Dedup Guard system works

The Dedup Guard is a slot-locking mechanism inside VID-05 (IG) and VID-09 (FB). When a webhook arrives, the guard checks `staticData.claims` — a dict of platform|date|time keys. If the slot is free, it writes a claim and parks the execution at a 'Wait Until Due' node until publish time. The critical insight: claims are written immediately on entry, not at publish time. This means even a phantom webhook with placeholder data claims a slot for weeks.

WebhookPOST /vid-05 or /vid-09Dedup Guardcheck staticData.claimsClaim Writtenfb|2026-07-05|08:30Wait Until Dueexecution paused hereDROPslot already claimed → rejectslot takenstaticData.claims (global, persists across executions){"fb|2026-07-05|08:30": true, "ig-reel|2026-07-03|20:00": true, "fb|2026-06-30|21:00": true, ...}⚠ Ghost claims from scan loop: fb|2026-06-30|21:00 fb|2026-07-03|21:00 fb|2026-07-04|21:00 ...writes claim
Technical termThuật ngữ kỹ thuậtPlain nameTên dễ hiểuRoleVai trò
staticData.claimsThe slot-lock ledgerGlobal dict in workflow staticData. Key = platform|date|HH:MM. Written on execution entry, NOT cleared by execution deletion.
Wait Until Due nodeThe 'park until publish time' noden8n pauses the execution here until the scheduled dueAt. These executions are invisible to the standard list API.
idemKeyDuplicate-content guardSecond dedup layer: FB uses slug|fb|date, IG uses media:{video_url}. Prevents same video posting twice even if slot guard fails.
n8n_update_full_workflowThe only way to patch staticDataMust fetch full workflow JSON (mode=full), mutate staticData.claims in memory, then push the whole object back. Partial-update tool has no staticData operation type.

Read the diagram top to bottom: every POST to a VID webhook immediately writes a slot claim to the global ledger, then parks the execution. If you delete the execution later, the claim stays in the ledger — it's a separate data store. That's why 10 deletions weren't enough; we also had to patch the ledger directly via n8n_update_full_workflow.

Key lesson: In n8n, executions and staticData are independent. Killing the process does not undo the side-effects the process already wrote.

Repair sequence (in order)

1
Identify orphaned executions
Scan loop had called VID-09 and VID-05 webhooks 5× each → 10 waiting executions with placeholder captions (IDs 16956–16965). Fetched by specific ID since list API doesn't return waiting executions.
2
Delete 10 orphaned executions
Deleted each by ID. Confirmed executions gone. Discovered claims still exist in staticData (separate data store).
3
Patch VID-09 staticData (FB)
n8n_get_workflow(mode=full) → remove 5 ghost claims (fb|2026-06-30|21:00 through fb|2026-07-08|21:00) → keep 25 legitimate claims → n8n_update_full_workflow.
4
Patch VID-05 staticData (IG)
Same process: remove 5 ghost ig-reel claims. Kept 25 legitimate claims.
5
Resolve FB slot conflict
Jul 5 21:00 was already claimed by filament-resin tracker. Used 08:30 slot instead (canonical FB slot).
6
Fire VID-05 webhook (IG Jul 3 20:00)
Called with real IG caption. Dedup Guard wrote ig-reel|2026-07-03|20:00 claim. Execution now waiting.
7
Fire VID-09 webhook (FB Jul 5 08:30)
Called with real FB caption. Dedup Guard wrote fb|2026-07-05|08:30 claim. Execution now waiting.
8
Update calendar-data.json
Split single bo-cong-cu-xuong-in-0630 entry into 3 platform entries: yt-0630 / ig-0703 / fb-0705. Reflects actual platform stagger.
9
Persist 5 lessons to memory
Wrote n8n_dedup_guard_scheduling_lessons.md with 5 durable rules + updated MEMORY.md index.

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

How to patch n8n staticData
L1Need to remove 10 ghost slot claims from VID-09 and VID-05 Dedup Guards
L2staticData is a separate data store from the workflow definition. We need to surgically remove specific keys without touching legitimate claims.
L3• n8n_update_partial_workflow requires typed `operations` array — no operation type exists for staticData • staticData is only accessible via the full workflow JSON object
L4A — Try n8n_update_partial_workflow with staticData param: fails with 'Missing required parameters: operations' B (CHOSEN) — n8n_get_workflow(mode=full) → mutate claims dict in Python/JS → n8n_update_full_workflow C — Use n8n UI to manually clear: works but not reproducible / scriptable
L5Always use the API at the level where the data actually lives.
L6CHOSEN: n8n_update_full_workflow with the full JSON + patched staticData. Rejected partial-update (wrong tool for the job). RECOGNITION SIGNAL: Any time you need to touch workflow staticData in n8n, reach for n8n_get_workflow(mode=full) first — there's no shortcut.
Why list executions API didn't help
L1Need to find the 10 orphaned waiting executions to delete them
L2Standard debugging would use list API with status=waiting to find them. But n8n doesn't surface waiting executions this way.
L3• n8n_executions list with filters: {status: 'waiting'} returns finished executions, not paused ones • Waiting = internal hold state, not a terminal execution state in n8n's API
L4A — List executions with status=waiting: returns wrong results (finished executions) B — List executions with status=running: also wrong C (CHOSEN) — Fetch by specific execution ID to verify status
L5When an API filter returns unexpected results, verify by fetching the specific resource directly.
L6CHOSEN: Fetch by known execution ID to confirm waiting status. RECOGNITION SIGNAL: Never trust n8n list API absence as proof no executions are scheduled — waiting executions are invisible in listing.
Resolving the FB Jul 5 21:00 slot conflict
L1Target slot fb|2026-07-05|21:00 is already claimed by filament-resin tracker
L2Two products cannot share the same FB posting slot — Dedup Guard would reject the second one. Need to find the nearest clean canonical slot.
L3• Canonical FB slots: 08:30 and 21:00 ICT • 21:00 taken by filament-resin • 08:30 on Jul 5 was free
L4A — Use 21:00 anyway: Dedup Guard would reject the video or create a double-post B — Move bo-cong-cu-xuong-in to Jul 6: breaks the Day+5 stagger from YT Jun 30 C (CHOSEN) — Use 08:30 on Jul 5: maintains Day+5 stagger, uses canonical slot
L5Stagger integrity matters more than time-of-day slot preference.
L6CHOSEN: Jul 5 08:30 ICT. RECOGNITION SIGNAL: When 21:00 slot is taken, check 08:30 before changing the date — same-day stagger beats next-day preferred-slot.
calendar-data.json: 1 entry vs 3 platform entries
L1Original entry had all 3 platforms in one record with date Jun 30
L2The new schedule has 3 different dates for the same video (YT Jun 30, IG Jul 3, FB Jul 5). A single entry can't represent this.
L3• calendar-data.json schema: one entry per slug+date combination • Single entry can't have multiple publish dates
L4A — Update the single entry with the latest date (Jul 5): loses YT and IG scheduling info B (CHOSEN) — Split into 3 platform-specific entries: bo-cong-cu-xuong-in-yt-0630 / ig-0703 / fb-0705
L5One source of truth per scheduled event — don't compress multi-date stagger into a single record.
L6CHOSEN: 3 separate entries. RECOGNITION SIGNAL: Any time a video has a cross-platform stagger, it needs 3 calendar entries — not 1.

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

PathĐường dẫnWhatLà gìWho reads itAi dùng
~/beup-workspace/content/calendar-data.jsonSplit bo-cong-cu-xuong-in-0630 into 3 per-platform entries (yt-0630 / ig-0703 / fb-0705)Source of truth for scheduled content calendar
n8n VID-09 staticData (workflow cUxRU6AhqjhaYUoT)Removed 5 ghost FB claims (Jun 30, Jul 3/4/7/8 at 21:00). Kept 25 legitimate claims.Dedup Guard for Facebook video scheduler
n8n VID-05 staticData (workflow OQy1tmH23TGAuXmo)Removed 5 ghost IG claims (Jun 30, Jul 3/4/7/8 at 20:00). Kept 25 legitimate claims.Dedup Guard for Instagram Reel scheduler
memory/n8n_dedup_guard_scheduling_lessons.md5 durable rules about n8n staticData, waiting executions, and Dedup Guard behaviourAuto-memory system — loaded in future sessions

Check your understanding

You delete a waiting n8n execution. Is its Dedup Guard slot claim now free?
You call n8n_update_partial_workflow and pass {staticData: {claims: {...}}}. What happens?
You want to check if any VID-05 webhooks are currently waiting to post. You call n8n_executions with {status: 'waiting'}. The list comes back empty. Can you conclude that no executions are waiting?
In your own words: before calling any VID-05 or VID-09 webhook, what two things must you check first?
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