Errors
Every error response has one shape, whatever the endpoint:
{ "error": { "code": "validation_error", "message": "caption is required", "field": "caption" }}| Key | Present | Notes |
|---|---|---|
code |
always | Stable, machine-readable. Branch on this |
message |
always | Human-readable. Log it, do not parse it |
field |
validation_error |
The offending body or query field |
details |
network_error |
Meta’s error object, as returned by Meta |
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
unauthorized |
401 | No Authorization header, wrong prefix, revoked key, or expired embed session |
Check the header and the key. For an embed session, mint a new one |
forbidden |
403 | The token is valid but does not reach this workspace or resource, or the endpoint needs a partner key | Use the right key kind. See the table in Authentication |
not_found |
404 | No such object within your scope. Objects from other workspaces look like this too | Check the id and the workspace |
validation_error |
400 | A field is missing, has the wrong type, or breaks a rule MissLess checks up front | Read field, fix the request |
unsupported |
400 | The network cannot do what you asked: text-only on Instagram, an option that does not apply, a media kind the network refuses | Change the request for that network |
conflict |
409 | The object is in a state that does not allow the action: patching a publishing or published post, publishing a canceled one, creating a webhook that already exists on a method that does not replace |
Read the current state first |
account_disconnected |
409 | A target account is expired, revoked or in error |
Send the user through a connect link, then retry |
rate_limited |
429 | Over 300 requests per minute per key, or 60 per minute per IP on unauthenticated endpoints | Wait Retry-After seconds |
network_error |
502 | Meta refused or failed the request | Inspect details. Retry only for transient causes |
network_error
Section titled “network_error”Meta’s response is passed through in details so you can act on it without guessing:
{ "error": { "code": "network_error", "message": "Meta refused the request", "details": { "message": "(#10) Application does not have permission for this action", "type": "OAuthException", "code": 10, "error_subcode": 2069019, "fbtrace_id": "AbCdEfGh123" } }}Common details.code values:
details.code |
Cause | Action |
|---|---|---|
| 190 | Token invalid or expired | The account will show expired or revoked; reconnect |
| 10, 200 | Missing permission | The user removed a permission at consent time; reconnect with all permissions on |
| 4, 17, 32, 613 | Meta rate limits | Back off; on Instagram check the 100 posts per 24 hours cap |
| 100 | Invalid parameter, often media | Check aspect ratio, format and size in Instagram rules |
| 1, 2 | Meta internal or temporary | Retry after a minute |
For asynchronous publishing, the same information lands on the post: targets[].error and last_error, and the post.failed or post.partial webhook.
Validation before Meta
Section titled “Validation before Meta”MissLess validates what it can before touching Meta, so most mistakes come back as validation_error in milliseconds, with field set:
field |
Typical message |
|---|---|
workspace |
workspace is required, or does not belong to this partner |
targets |
at least one target is required, or an account id is not in the workspace |
media |
Instagram targets need at least one media item, an Instagram target has a non-JPEG image, or a media id is not in the workspace |
caption |
caption is required for a Facebook text post, or longer than 2,200 characters |
schedule_at |
must be an ISO-8601 timestamp in the future |
options |
unknown option for network |
url |
not https, not reachable, or a private address |
file |
unsupported format or over the size limit |
Handling pattern
Section titled “Handling pattern”try { return await api('/posts?wait=true', { method: 'POST', body });} catch (err) { switch (err.code) { case 'validation_error': return showFieldError(err.field, err.message); case 'account_disconnected': return promptReconnect(); case 'rate_limited': return retryAfter(err.retryAfter); case 'network_error': return showMetaError(err.details?.message); case 'unauthorized': case 'forbidden': throw err; // configuration problem, page someone default: throw err; }}