meta-debug
Skill1 starsChanged 3 months agoMITView on GitHub
Diagnose a broken Meta Graph API integration by isolating which link failed — OAuth returning "URL blocked", a token that expired or was never exchanged for a long-lived one, missing scopes, campaign sync returning nothing, insights coming back empty, numbers that disagree with Ads Manager, ad creation rejected with a vague error, Instagram posts that never publish, or DM webhooks that never fire or fire twice. Use when the Meta integration worked and stopped, when it never worked, or before changing code to "try something".
2 other projects use a version of this.
Adopted by
Most recently taken up by
- arthjean/skillstheir own version, first seen Nov 1, 2026
- ha0wan9/skillstheir own version, first seen Aug 12, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- arthjean/skills★ 3same name
- ha0wan9/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository2 other files it gives its agents.
- meta-graph-apiSkill
- meta-implementSkill
The file
We split this skill into the rules it gives an agent
7 rules. About 0 are common, 5 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 36
Always start here. debug_token is the single most useful call in the whole API.
- Very specific: line 44
Read three fields: is_valid, expires_at, and scopes.
- Distinctive: line 53
If the account you need is not in that list, no amount of code will find it — it is a Business Manager permission problem, not a bug.
- Distinctive: line 90
Read the function logs:
- Distinctive: line 99
Not a bug in 90% of cases: it is the attribution window.
- Distinctive: line 201
If two links are still possible, say which check separates them.
- Distinctive: line 210
Never flip a live campaign's status while debugging.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Diagnose a Meta Graph API integration
Meta errors are famously unhelpful — they frequently name neither the field nor the entity at fault. The cure is to isolate the link before reading code.
Almost every failure is one of four things: the wrong token, a missing scope, an id from the wrong account, or a version/format mismatch.
Reference: ../meta-graph-api/references/, examples in
../meta-graph-api/assets/examples/.
The chain
[1] Meta app App ID/Secret, products added, redirect URI registered, scopes reviewed
[2] OAuth code → short-lived token → LONG-LIVED token → stored with expires_at
[3] Assets ad accounts / pages / IG accounts discovered and linked to a client
[4] Graph calls right token type, right version, right account for the entity
[5] Database write sync job writes campaigns/adsets/ads under the right tenant
[6] Frontend RLS + GRANT let this user read the rows
[7] Webhooks subscribed, verify_jwt=false, HMAC valid, dedupedStep 1 — the two commands that answer most questions
Always start here. debug_token is the single most useful call in the whole API.
# What is this token, does it still work, and what was ACTUALLY granted?
curl -s "https://graph.facebook.com/v21.0/debug_token\
?input_token=$USER_TOKEN&access_token=$APP_ID|$APP_SECRET" | jq '.data'Read three fields: is_valid, expires_at, and scopes. Requested ≠ granted —
a user can approve a subset, and the app then fails on exactly the missing one.
# Does the token see the accounts you expect?
curl -s "https://graph.facebook.com/v21.0/me/adaccounts\
?fields=id,name,account_status&access_token=$USER_TOKEN" | jq '.data[]'If the account you need is not in that list, no amount of code will find it — it is a Business Manager permission problem, not a bug.
Step 2 — by symptom
"URL blocked" / OAuth fails immediately
Link [1]/[2]. The redirect_uri is compared string for string between the
auth dialog and the token exchange, and both must match a URI registered in the
app.
- One trailing slash apart → blocked.
httpvshttps→ blocked.- Registered
https://app.example.com/auth/callback, senthttps://app.example.com/auth/callback/→ blocked. - Works in production, fails on localhost → localhost is not in the allowlist.
"The token worked for weeks and stopped"
Link [2]. The OAuth exchange returns a short-lived token (~1–2h). If nobody ran the second exchange, what got stored expires far sooner than the 60 days the code assumed.
select id, expires_at, expires_at < now() as expirado from facebook_integrations;Fix: grant_type=fb_exchange_token. Then re-connect the account — an expired
token cannot be renewed, only replaced.
"Sync returns nothing" / "insights are all zero"
Link [4]. In order:
debug_token→ isads_readinscopes? Empty insights with no error is the signature of a missing scope.- Did the account actually deliver in that period? Zero is a legitimate answer.
- Read the function logs:
Meta errors arrive assupabase functions logs sync-meta-campaigns --limit 50{ error: { message, type, code, error_subcode } }—codeanderror_subcodeare what to search for, not the message.
"The numbers don't match Ads Manager"
Not a bug in 90% of cases: it is the attribution window. Ads Manager defaults to 7-day click / 1-day view; a query without
.action_report_time(conversion).action_attribution_windows(["7d_click","1d_view"])reports different numbers and is not wrong, just different. Also confirm both sides use the same time zone (the ad account's, not the user's) and the same date range boundaries.
Contract: ../meta-graph-api/assets/examples/graph-api-insights.md.
"Creating an adset/ad fails with a vague error"
Link [4]. Three usual causes:
- Account mismatch — the adset's ad account differs from its campaign's.
Meta's error names neither. The module ships
check-adset-account-mismatchesfor exactly this. - Content-Type — Marketing API creation POSTs need
application/x-www-form-urlencoded. A JSON body fails obscurely. - Nested objects must be JSON strings inside the form body (
targeting,promoted_object), not nested form fields.
Compare your payload against
../meta-graph-api/assets/examples/criacao-campanha-requests.md.
"The Instagram post never appears"
Link [4]. Publishing is two calls:
POST /{igUser}/media → returns a container id
POST /{igUser}/media_publish → actually publishes itGetting a container id back and stopping there is the most common bug. Also: containers expire in ~24h; the IG account must be Professional and linked to a Facebook Page; publishing uses the page token, not the user token.
"DM webhook never fires"
Link [7], in order:
supabase functions logs instagram-messaging-webhook --limit 50- No entries at all → the app is not subscribed to the
messagesfield for that page, or the callback URL failed Meta's verification handshake. - 401/403 →
verify_jwtis stilltrue. Meta webhooks must befalse; the security comes from HMAC, not from JWT. - Entries rejected by signature → the HMAC uses the app secret over the raw body. Parsing the JSON first and re-serializing it changes the bytes and the signature never matches.
"Every DM arrives twice and the automation fires twice"
Meta redelivers. Deduplicate by message id:
select external_id, count(*) from instagram_messages
group by 1 having count(*) > 1;Fix: UNIQUE (external_id) and an upsert. Without it, a redelivery duplicates
the message and re-fires the keyword automation — the user gets two replies.
"Everything returns an empty array, no error"
Link [6] — the signature failure of this stack:
select grantee, privilege_type from information_schema.role_table_grants
where table_name = 'campaigns'; -- expect: authenticated
select public.get_user_company_ids(auth.uid()); -- or your renamed helper
select policyname, qual from pg_policies where tablename = 'campaigns';A policy that queries an RLS-protected table directly instead of going
through the SECURITY DEFINER helper fails silently. Also check company_id IS
NULL rows: a write without a tenant vanishes from the UI with no error.
"The inbox needs a manual refresh"
The row was written; Realtime is not delivering.
select tablename from pg_publication_tables where pubname = 'supabase_realtime';
alter publication supabase_realtime add table instagram_messages;
alter table instagram_messages replica identity full;Step 3 — report before fixing
Three lines: which link broke, the evidence (log line, debug_token
output, the exact Graph error code/error_subcode), and the fix. Ask
before anything that deploys, migrates, or touches a live ad entity.
If two links are still possible, say which check separates them.
Step 4 — fixing
- Compare the project's file with the packaged artifact in
../meta-graph-api/assets/— the difference usually is the bug. - Fix one link at a time and re-run that link's check.
- Never paste a token, App Secret, ad account id or message content into a report.
- Never flip a live campaign's status while debugging. It spends real money.
Things that are not bugs
- Zero insights for a paused campaign with no delivery in the period.
account_statusother than 1 — the ad account is disabled or unsettled on Meta's side; the integration is fine.- Scopes missing in Development mode — production scopes require App Review.
- A page you can see in Business Manager but not via
/me/accounts— page access is granted per user; check the Business Manager role. expires_ataround 60 days out — that is a correctly exchanged long-lived token, not a bug.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

