[object Object]

← back to Marketing Command Center

instagram: poll media container status until FINISHED before publish — fixes 'Media ID is not available' (Meta processes images async)

c1d7a1d3838c782e8a78f7e0be41ac1ed0ae1a2e · 2026-07-17 09:58:38 -0700 · Steve Abrams

Files touched

Diff

commit c1d7a1d3838c782e8a78f7e0be41ac1ed0ae1a2e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jul 17 09:58:38 2026 -0700

    instagram: poll media container status until FINISHED before publish — fixes 'Media ID is not available' (Meta processes images async)
---
 modules/channels/index.js | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/modules/channels/index.js b/modules/channels/index.js
index 2d73b3f..f535259 100644
--- a/modules/channels/index.js
+++ b/modules/channels/index.js
@@ -412,6 +412,17 @@ async function postInstagram(content, opts = {}) {
     });
     const cj = await create.json().catch(() => ({}));
     if (!create.ok || !cj.id) { results.push({ igId: t.igId, name: t.name, ok: false, error: cj.error?.message || 'media create failed' }); continue; }
+    // Meta processes the image container ASYNC — publishing before it's FINISHED throws
+    // "Media ID is not available". Poll status_code until FINISHED (or ERROR/timeout).
+    let ready = false;
+    for (let i = 0; i < 12; i++) {
+      await new Promise(r => setTimeout(r, i === 0 ? 1500 : 2500));
+      const s = await fetch(`${GRAPH}/${cj.id}?fields=status_code&access_token=${encodeURIComponent(t.token)}`);
+      const sj = await s.json().catch(() => ({}));
+      if (sj.status_code === 'FINISHED') { ready = true; break; }
+      if (sj.status_code === 'ERROR' || sj.status_code === 'EXPIRED') { break; }
+    }
+    if (!ready) { results.push({ igId: t.igId, name: t.name, ok: false, error: 'media container not ready (still processing or errored)' }); continue; }
     const pub = await fetch(`${GRAPH}/${t.igId}/media_publish`, {
       method: 'POST', headers: { 'content-type': 'application/json' },
       body: JSON.stringify({ creation_id: cj.id, access_token: t.token }),

← 47459ec cc-import: stage gated FM-client → Constant Contact import (  ·  back to Marketing Command Center  ·  chore: lint, refactor (drop dead env()), v1.4.0 (session clo d77d6ee →