Skip to main content
Socialift
Documentation sections
Concepts

Error handling and automatic retries

The Socialift SDK maps every platform's errors onto one hierarchy: AuthError for invalid tokens, PermissionError for missing permissions, RateLimitError for limits, NotFoundError for missing objects and TransportError for network failures. Rate-limit errors, HTTP 429 and 5xx responses and dropped connections are retried automatically by RetryPolicy: 3 attempts by default, exponential backoff from 0.5 seconds, with jitter.

Updated:

Which exceptions should you catch?

Catch the specific classes first and the generic ApiError last. AuthError: send the user to reconnect. PermissionError: fix scopes or App Review — retrying will not help. RateLimitError: try again later using retry_after or your own delay. TransportError: a network problem, usually temporary. License errors (LicenseError) are a separate branch and are not ApiErrors.

Exception hierarchy and attributes

Exception hierarchy and attributes — Capability matrix by platform
ClassParentAttributesMeaning
SocialiftErrorExceptionnoEvery SDK error, license errors included
ConfigurationErrorSocialiftErrornoA setting is missing or wrong (for example an empty app_id); raised before any request
ApiErrorSocialiftErrormessage, provider, status, code, subcode, payload, urlThe platform API returned an error
AuthErrorApiErrorsame as ApiErrorToken is invalid, expired or revoked
PermissionErrorApiErrorsame as ApiErrorMissing scope, App Review or chat rights
RateLimitErrorApiErrorApiError + retry_after (seconds or None)Rate limit — wait and try again
NotFoundErrorApiErrorsame as ApiErrorThe object does not exist or is not visible to the token owner
TransportErrorSocialiftErrorurlConnection dropped, DNS or timeout — no HTTP status
LicenseErrorSocialiftErrornoLicense errors — see the Authentication page

yesnoScroll the table sideways

Notes and limits

socialift.PermissionError shares its name with Python's built-in PermissionError, so import it under an alias: from socialift import PermissionError as ScopeError. str(exc) looks like [provider] message (HTTP status, code=...).

Which class does a platform error map to?

The SDK classifies errors by these rules; anything that matches none of them stays a plain ApiError.

Which class does a platform error map to? — Capability matrix by platform
ClassMeta (Facebook, Instagram, Ads)LinkedInTikTokTelegram Bot API
RateLimitErrorcode 4, 17, 32, 613 or HTTP 429HTTP 429rate_limit_exceeded, spam_risk_too_many_posts, spam_risk or HTTP 429HTTP 429 or parameters.retry_after in the response
AuthErrorcode 190, 102 or HTTP 401HTTP 401access_token_invalid, access_token_expired, invalid_access_token, token_not_found or HTTP 401HTTP 401 or “unauthorized” in the description
PermissionErrorcode 10, 200–299 or HTTP 403HTTP 403scope_not_authorized, scope_permission_missed, unaudited_client_can_only_post_to_private_accounts, url_ownership_unverified, spam_risk_user_banned_from_posting or HTTP 403HTTP 403 or “forbidden” in the description (for example, the user blocked the bot)
NotFoundErrorcode 803 or HTTP 404HTTP 404publish_id_not_found, video_not_found or HTTP 404HTTP 404 or “not found” in the description
Notes and limits

Check order: rate limit → auth → permission → not found. For Meta, exc.code is error.code, exc.subcode is error_subcode, and the message comes from error_user_msg when present, otherwise message. For LinkedIn, exc.code is serviceErrorCode. For TikTok the string code wins over the HTTP status, exc.code stays empty, the original code is in exc.payload, and log_id is appended to the message. For Telegram, exc.code is error_code, and the bot token is masked in exc.url.

How are Telegram account (MTProto) errors translated?

TelegramAccount converts Telethon exceptions into the same hierarchy, so one except block covers the bot, the account and the other platforms.

RateLimitError
FloodWaitError and other Flood… errors; code=420, and retry_after holds the seconds Telegram asked for. The SDK does not sleep for you, so a 20-minute wait never freezes your app — you decide.
AuthError
AuthKeyUnregisteredError, SessionRevokedError, SessionExpiredError, UserDeactivatedError, AuthKeyDuplicatedError, UnauthorizedError — the session is invalid and the account must be reconnected (status=401).
PermissionError
ChatAdminRequiredError, ChatWriteForbiddenError, UserBannedInChannelError, ChannelPrivateError, UserPrivacyRestrictedError, MessageDeleteForbiddenError, AdminsTooMuchError, UserNotParticipantError, ChatRestrictedError (status=403).
NotFoundError
UsernameNotOccupiedError, PeerIdInvalidError, MessageIdInvalidError, ChannelInvalidError, UserIdInvalidError, InviteHashInvalidError, plus the ValueError Telethon raises when it cannot find an entity (status=404).
TransportError
ConnectionError, OSError, TimeoutError — the connection to Telegram was lost.

What do Meta error codes mean?

A Meta Graph API error arrives in an error object with message, type, code, error_subcode, error_user_title, error_user_msg and fbtrace_id. The SDK keeps the whole response in exc.payload, including the fbtrace_id that Meta support asks for.

Per Meta's documentation: 4 is app-level throttling, 17 user-level throttling, 32 page-level throttling in the Pages API, and 613 a custom endpoint limit; 190 means the access token expired or is invalid, 102 is an API session error; 10 means a permission was not granted or was revoked, and 200–299 are specific permission errors.

Meta does not document a standard Retry-After header for throttling; for Business Use Case limits the estimated wait arrives as estimated_time_to_regain_access (in minutes) inside the X-Business-Use-Case-Usage header. That is why retry_after on a Meta RateLimitError is usually None — choose the delay yourself.

Which errors are retried automatically?

In the Facebook, Instagram, Ads, LinkedIn, TikTok and Telegram clients the SDK retries in three cases: RateLimitError; an ApiError with HTTP 429, 500, 502, 503 or 504; and TransportError (aiohttp connection errors and timeouts). AuthError, PermissionError, NotFoundError and other 4xx responses are raised immediately. When attempts run out, the last error is raised.

Delay: if the provider sent a Retry-After header, the SDK waits exactly that long, and this value is not capped by backoff_max. Otherwise the RetryPolicy formula applies. Each retry after an HTTP error is logged at WARNING level on the socialift logger.

Exceptions: when TikTok reports an error inside an HTTP 200 body, it is raised without retries. TelegramAccount (MTProto) does not use RetryPolicy: a FloodWaitError becomes a RateLimitError immediately, and connection attempts are governed by the connection_retries parameter.

Timeouts and connection errors are retried for POST requests too. If the request reached the platform but the response was lost, the action can run twice — for writes where a repeat is harmful, use a separate client with RetryPolicy(attempts=1).

RetryPolicy defaults

RetryPolicy defaults — Capability matrix by platform
ParameterDefaultMeaning
attempts3Total attempts, including the first one
backoff_base0.5Base of the first delay, seconds
backoff_max30.0Upper bound of the delay (before jitter), seconds
jitterTrueMultiplies the delay by a random factor of 0.5–1.5
timeout (client parameter)30.0Total timeout of a single HTTP request, seconds
Notes and limits

Formula: min(backoff_base × 2^(n−1), backoff_max), then jitter. With the defaults the SDK waits roughly 0.25–0.75 s after the first failure and 0.5–1.5 s after the second, and the third failure is raised.

How do you pass your own RetryPolicy?

retry.py
import os
from socialift import Facebook, RetryPolicy, Telegram

# Background jobs: more attempts, longer delays and timeout
patient = RetryPolicy(attempts=5, backoff_base=1.0, backoff_max=60.0)
fb = Facebook.from_env(retry=patient, timeout=60.0)

# Writes where a repeat is harmful: no retries
once = RetryPolicy(attempts=1)
bot = Telegram(token=os.environ["TELEGRAM_BOT_TOKEN"], retry=once)

retry= and timeout= are accepted by every HTTP client (Facebook, Instagram, LinkedIn, TikTok, Telegram, Ads), as well as by their from_env() and by Instagram.via_facebook(). The policy is set per client — there is no global setting.

Once retries are exhausted, the error reaches your code. Here is a pattern for a background job.

handle_errors.py
import logging
from socialift import ApiError, AuthError, RateLimitError, TransportError
from socialift import PermissionError as ScopeError

log = logging.getLogger("app")


async def sync_profile(ig, account, jobs):
    # account and jobs are your app's objects (a DB record and a job queue)
    try:
        return await ig.profile(account.token, ig_user_id=account.ig_user_id)
    except AuthError:
        account.needs_reconnect = True                # send the user through OAuth again
    except ScopeError as exc:
        log.error("Missing permission: %s", exc)      # scope or App Review — retrying is pointless
    except RateLimitError as exc:
        await jobs.retry_later(account.id, delay=exc.retry_after or 300)
    except TransportError:
        await jobs.retry_later(account.id, delay=30)
    except ApiError as exc:
        log.error("%s status=%s code=%s", exc.provider, exc.status, exc.code)

The except order matters: AuthError, PermissionError and RateLimitError subclass ApiError, so they come first.

The SDK does not refresh an expired token for you. Refresh methods: Instagram.refresh_token (Instagram Login), TikTok.refresh_token, LinkedIn.refresh; for a revoked token the user logs in again.

Error handling checklist

  • Do not retry AuthError or PermissionError — they need a reconnect or a configuration fix.
  • When a RateLimitError has no retry_after, requeue the job with a delay measured in minutes.
  • Log str(exc), exc.status, exc.code and exc.payload. Do not log exc.url: for Meta requests it contains the access_token query parameter.
  • Catch LicenseError separately from ApiError — it is a license problem, not a platform one.
  • ConfigurationError points to a bug in code or environment variables; surface it at startup.

Frequently asked questions

How long should I wait when retry_after is None?

That is normal for Meta, which does not document a standard Retry-After header for throttling. Pick a delay yourself (a few minutes, for example) and requeue the job. The SDK's built-in retries are measured in seconds and are not meant to ride out a long throttle.

Does the SDK refresh the token by itself after an AuthError?

No. The SDK neither stores nor automatically refreshes tokens. Refreshing before expiry, or sending the user back through OAuth, is your application's job. TokenBundle.is_expiring() helps you decide when to refresh.

Why does a PermissionError never succeed on retry?

Because the cause is the permissions, not the request: the needed scope was not requested, the app has not passed the platform's review (Meta App Review, for example), or the account lacks rights in a Telegram chat. Fix the configuration and reconnect the user with the right permissions.

Can I turn retries off completely?

Yes: pass retry=RetryPolicy(attempts=1) to the client. Every error is then raised right after the first attempt, which suits actions where a repeat is harmful, such as publishing a post or sending a message.

Where do I find the TikTok error code?

TikTok returns a string code such as access_token_invalid. The SDK maps it to a class but does not copy it to exc.code: the original code is in exc.payload, and the log_id TikTok support asks for is appended to the message.

What is the difference between TransportError and ApiError?

ApiError means the platform answered with an error, so there is a status and often a code. TransportError means no answer arrived at all (dropped connection, DNS, timeout), so there is no status. TransportError is always retried; ApiError only for rate limits, 429 and 5xx.

Sources

  1. Meta: Graph API error handling
  2. Meta: Graph API rate limiting
  3. LinkedIn API error handling
  4. TikTok API v2 error handling
  5. Telegram Bot API

Related pages