Skip to content

fix(facebook): map five recurring Graph API rejections instead of "Unknown Error" - #2045

Closed
giladresisi wants to merge 2 commits into
stagingfrom
fix/facebook-error-mappings
Closed

giladresisi wants to merge 2 commits into
stagingfrom
fix/facebook-error-mappings

Conversation

@giladresisi

Copy link
Copy Markdown
Collaborator

What kind of change does this PR introduce?

Bug fix (backend, Facebook provider error mapping). Adds five branches to handleErrors in facebook.provider.ts so responses that today surface as "Unknown Error" get a readable message and the right outcome:

  • 190 / subcode 459 (user checkpointed): bad-body, tells the user to log in at facebook.com and resolve the security check.
  • 190 / subcode 492 (no role on the Page): bad-body, tells the user to get a Page role from an admin, then reconnect.
  • 190 "Any of the pages_read_engagement, ... permission(s) must be granted before impersonating a user's page": refresh-token, so the channel is flagged for reconnection with all permissions.
  • 100 / subcode 33 "Object with ID ... does not exist": bad-body, the target Page or post is gone, no point retrying.
  • Facebook's HTML outage page ("Sorry, something went wrong. We're working on it"): retry.

The retry type is added to the method's return union. The new branches sit before the existing loose '490' substring match so it cannot swallow them. No existing mapping changed, and the generic fetch/retry logic in social.abstract.ts is untouched.

Why was this change needed?

The production Errors table has 1,827 Facebook rows in the last 45 days whose only message is "Unknown Error". Decoding the raw Graph API bodies stored in the failure details shows they are a handful of well-defined responses. The five mapped here account for 655 of them:

  • 318 are 100/33 "does not exist". Almost all target an integration that was soft-deleted and re-added, so the stored page id no longer exists on Facebook. These were being retried for nothing. Blocking post creation on deleted channels landed in fix(posts): reject post creation on soft-deleted channels #1921; the workflow-side guard on integration.deletedAt is deliberately left for the next workflow version bump.
  • 190 are the missing pages_* permissions body. Meta's error reference lists code 200-299 / missing permissions as "handle the missing permissions", which for us means re-granting them in the login dialog, hence refresh-token.
  • 52 are 190/492 and 45 are 190/459. Per Meta's error reference these need the user to fix a Page role or a checkpoint on Facebook itself, so a reconnect prompt would be misleading; they now fail with the Facebook-side instruction.
  • 50 are the HTML outage page. It has no JSON message, so it could never be mapped by code; the visible text is stable and Meta's guidance for its downtime codes is to wait and retry.

The rest of the "Unknown Error" volume is the "Page not accessible" token error (#1881) and the (#200) permission family (#1892), both covered by their own PRs.

Meta error reference: https://developers.facebook.com/docs/graph-api/guides/error-handling

Other information:

Verified by running the updated handleErrors against every distinct Facebook "Unknown Error" body from production in the last 45 days: the five responses above map to the intended type and message, every other body still returns undefined, and all pre-existing mappings produce the same results as before. tsc on nestjs-libraries reports no new errors.

QA

  1. In libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts, call new FacebookProvider().handleErrors(body, 400) (for example from a scratch ts-node script at the repo root) with {"error":{"message":"x","code":190,"error_subcode":459}}; expect type: 'bad-body' and a message about resolving a security check at facebook.com.
  2. Same call with {"error":{"message":"x","code":190,"error_subcode":492}}; expect type: 'bad-body' and a message about needing a role on the Page.
  3. Same call with {"error":{"message":"Any of the pages_read_engagement, pages_manage_metadata permission(s) must be granted before impersonating a user's page.","code":190}}; expect type: 'refresh-token'.
  4. Same call with {"error":{"message":"Unsupported post request. Object with ID '123' does not exist, cannot be loaded due to missing permissions, or does not support this operation.","code":100,"error_subcode":33}}; expect type: 'bad-body' and a message saying the target no longer exists.
  5. Same call with the string <html><title>Facebook | Error</title><body>Sorry, something went wrong. We're working on it and we'll get it fixed as soon as we can.</body></html>; expect type: 'retry'.
  6. Same call with {"error":{"message":"(#200) If posting to a group, requires app being installed in the group, and either publish_to_groups permission with user token, or pages_read_engagement","code":200}}; expect undefined (the permissions branch must not catch (#200) bodies that merely list permission names).
  7. Same call with {"error":{"message":"Error validating access token","code":190}}; expect the pre-existing refresh-token result, unchanged.
  8. Optional end to end: connect a Facebook Page, delete the channel from the dashboard while a post is still scheduled to it, then re-add the same Page. When the old post runs, the calendar tooltip shows the "no longer exists" message and the workflow does not retry the activity.

Checklist:

Put a "X" in the boxes below to indicate you have followed the checklist;

  • I have read the CONTRIBUTING guide.
  • I have signed the Contributor License Agreement (CLA) (ICLA for individuals, CCLA for entities).
  • I confirm I have not used AI to submit this PR or generate code for it.
  • I checked that there were no similar issues or PRs already open for this.
  • This PR fixes just ONE issue
  • I have filled in the QA section above with real steps to verify this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GBHogYQMvY5zHT7qkdUQAT

…known Error"

Adds handleErrors branches for 190/459 (checkpoint), 190/492 (no Page role),
the 190 missing pages_* permissions body, 100/33 object does not exist, and
Facebook's HTML outage page, so users see the reason and the right outcome
(fail, reconnect, or retry) instead of "Unknown Error".
@postiz-contribution postiz-contribution Bot added the contribution:approved Approved contributor label Sep 9, 2026
@strix-security

strix-security Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Strix Security Review

No security issues found.

Updated for d15b736.


Reviewed by Strix
Re-run review · Configure security review settings

@postiz-agent

postiz-agent Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues
✅ Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment on lines +231 to +239
if (
body.indexOf('"error_subcode":33') > -1 &&
body.indexOf('does not exist') > -1
) {
return {
type: 'bad-body' as const,
value:
'The Facebook Page or post this was targeting no longer exists, please reconnect the channel and schedule again',
};

This comment was marked as outdated.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid, fixed in the follow-up commit: the three subcode checks (33, 459, 492) now test /"error_subcode":NNN\b/ so a trailing digit no longer matches. Re-verified against all 1,827 production bodies (same 655 mapped, everything else still undefined) plus negative cases for 330, 4590 and 4920.

"error_subcode":33 as a substring also matched 330 and 331; the same held for
459 and 492. Test the three subcodes with a trailing word boundary instead.
@giladresisi

Copy link
Copy Markdown
Collaborator Author

Replaced by #2127, the same change rebased onto main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution:approved Approved contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant