Skip to content

shalvah/twitter-error-handler

Repository files navigation

twitter-error-handler

npm version npm

Wrap errors from Twitter API calls like a boss. 😎

Okay, what's this?

Let's take a look. You're writing a service where you make calls to the Twitter API. Suppose you're using the package Twit. Your code would look something like this:

return t.get('statuses/mentions_timeline', options)
    .then(r => r.data)
    .then(tweets => tweets.map(processTweet));

But how do you handle errors from the Twitter API? Maybe your API token is wrong, or your app has been suspended, or you've hit a rate limit. Well, here's one way:

return t.get('statuses/mentions_timeline', options)
    .catch(e => {
        const error = JSON.stringify(e);
        if (error.includes("Invalid or expired token")) {
            // uh-oh, your token is wrong, do stuff
        } else if (error.includes("Invalid / suspended application")) {
            // This is very bad, as it means your app is broken for its users
        } else if (error.includes("Rate limit exceeded")
            || error.includes("User is over daily status update limit")) {
            // implement a backoff so Twitter doesn't suspend your app
        } // you get the idea...
    })
    .then(r => r.data)
    .then(tweets => tweets.map(processTweet));

But this isn't very optimal, is it? What happens if Twitter changes their error messages? Also, that code makes my eyes bleeeed. Here's what this package lets you do:

const {wrapTwitterErrors, errors, codes} = require('twitter-error-handler');
return t.get('statuses/mentions_timeline', options)
    .catch(e => wrapTwitterErrors(e, 'statuses/mentions_timeline'))
    .catch(e => {
        if (e instanceof errors.ProblemWithAuth) {
            // uh-oh, your token is wrong, do stuff
        } else if (e instanceof errors.ProblemWithAppOrAccount) {
            // you need to take action
        } else if (e instanceof errors.RateLimited) {
            // implement a backoff so Twitter doesn't suspend your app
        }
    })
    .then(r => r.data)
    .then(tweets => tweets.map(processTweet));

If you mix in the aargh package, even neater:

return t.get('statuses/mentions_timeline', options)
    .catch(e => wrapTwitterErrors(e, 'statuses/mentions_timeline'))
    .catch(e => {
        return aargh(e)
            .type(ProblemWithAuth, (e) => {
                // uh-oh, your token is wrong, do stuff
            })
            .type(ProblemWithAppOrAccount, notifyMe)
            .type(RateLimited, backOff)
            .throw();
    })
    .then(r => r.data)
    .then(tweets => tweets.map(processTweet));

Installation

npm i twitter-error-handler

Usage

This package wraps the errors into a bunch of classes that represent the kinds of errors you can get from Twitter. To use this package, call the wrapTwitterErrors function with an endpoint and a response object within your first catch (or try/catch or however you handle errors) statement after your Twitter API call.

  • The endpoint is the path to the API you called. It's helpful so you can easily track what calls triggered what errors in your logs.

  • The response object is the original Twitter API response, as a JavaScript object. This package was designed to work with Twit (a very good Twitter API client). With Twit, the response object you pass in is the error in the catch block (as shown in the examples above). To use any other Twitter API client, you only need to pass in an endpoint and a response object matching the description above.

Output

When you call the wrapTwitterErrors function within a catch block, it wil perform checks on any errors passed to it and wrap the errors into a special Error object, and then throw it. The idea is to represent all possible Twitter API errors as an instance of one of the following:

  • ProblemWithYourAppOrAccount: This covers cases where your Twitter app or user account is having issues (for instance, account locked or app suspended by Twitter). You'll probably want to take action ASAP.
    Covers the following errors:
    • ACCOUNT_LOCKED_TEMPORARILY (326)
    • ACCOUNT_SUSPENDED (64)
    • APP_SUSPENDED (416)
    • APP_MUZZLED (261)
    • CALLBACK_URL_NOT_APPROVED (415)
    • DESKTOP_APPLICATIONS_ONLY_SUPPORT_OOB_OAUTH (417)
    • CLIENT_NOT_PERMITTED_TO_DO_THAT (87)
    • APP_NOT_ALLOWED_TO_ACCESS_DMS (93)
    • YOUR_CREDENTIALS_DONT_COVER_THIS (220)
    • WE_FELT_LIKE_FLAGGING_YOU (226)
  • RateLimited: For errors caused by hitting a rate limit. You should implement backoff to avoid being banned by Twitter. Covers:
    • RATE_LIMIT_EXCEEDED (88)
    • HIT_TWEET_LIMIT (185)
    • CANT_FOLLOW_MORE_PEOPLE_NOW_SO_CHILL (161)
    • REACHED_LIMIT_FOR_SPAM_REPORTS (205)
  • ProblemWithAuth: For errors caused by authentication issues. Covers:
    • INVALID_OR_EXPIRED_TOKEN (89)
    • AUTH_FAILED (32)
    • AUTH_FAILED_DUE_TO_TIMESTAMPS (135)
    • UNABLE_TO_VERIFY_CREDENTIALS (99)
    • BAD_AUTH_DATA (215)
  • ProblemWithTwitter: For internal server errors and "Twitter is over capacity" errors. It's recommended to wait a few minutes before retrying. Covers:
    • TWITTER_NEEDS_A_BREAK (130)
    • TWITTERS_DOWN_SORRY (131)
  • ProblemWithPermissions: For errors caused by permissions issues. For example, not allowed to view a protected tweet or DM a user. Covers:
    • CANT_DM_THIS_USER (150)
    • COULDNT_DM (151)
    • TWEET_PROTECTED (179)
    • REPLIES_RESTRICTED (433)
    • BLOCKED_BY_USER (136)
    • OWNER_MUST_ALLOW_DMS_FROM_ANYONE (214)
    • CANT_DM_THE_FELLA (349)
    • TWEET_RESTRICTED_BY_TWITTER (425)
  • BadRequest: For errors caused by bad requests (invalid parameters, overly long values, invalid urls etc) .
    Covers the following errors:
    • INVALID_COORDINATES (3)
    • NO_DATA_FOR_COORDINATES_AND_RADIUS (7)
    • NO_DATA_FOR_THAT_ID (8)
    • MUST_PROVIDE_VALID_GEO_DATA (12)
    • CANT_REPORT_YOURSELF_FOR_SPAM (36)
    • REPLIED_TO_UNAVAILABLE_TWEET (385)
    • MISSING_QUERY_PARAMS (25)
    • PARAMETER_MISSING (38)
    • ATTACHMENT_URL_INVALID (44)
    • TWEET_TOO_LONG (186)
    • DUPLICATE_TWEET (187)
    • INVALID_URL_PARAMETER (195)
    • ACCOUNT_UPDATE_FAILED (120)
    • ALREADY_LIKED_THAT_SHIT (129)
    • ALREADY_FOLLOWED (160)
    • USER_NOT_IN_THE_LIST (110)
    • COULDNT_DETERMINE_SOURCE_USER (163)
    • DEVICE_ERROR (203)
    • HAHA_CANT_MUTE_YOURSELF (271)
    • CANT_UNMUTE_BECAUSE_YOU_WERENT_MUTING (272)
    • GIFS_NOT_ALLOWED_WITH_OTHER_IMAGES (323)
    • MEDIA_ID_GOT_A_PROBLEM (324)
    • DIDNT_FIND_A_MEDIA_ID (325)
    • ALREADY_RETWEETED (327)
    • DM_TOO_LONG (354)
    • SUBSCRIPTION_ALREADY_EXISTS (355)
    • ONLY_ONE_ATTACHMENT_TYPE_ALLOWED (386)
    • YOU_NEED_TO_ENABLE_COOKIES (404)
    • INVALID_URL (407)
    • INVALID_CONVERSATION_CONTROLS (431)
  • NotFound: For when the requested resource (eg Tweet/user) wasn't found.
    Covers the following errors:
    • NO_LOCATION_FOR_THAT_IP_ADDRESS (13)
    • NO_USER_MATCHES_THOSE_TERMS (17)
    • NOT_FOUND (34)
    • COULDNT_FIND_USER (50)
    • USER_NOT_FOUND_IN_THE_LIST (109)
    • NO_SUCH_TWEET (144)
    • USER_SUSPENDED (63)
    • TWEET_UNAVAILABLE (422)
    • TWEET_UNAVAILABLE_FOR_VIOLATING_RULES (421)
  • TwitterApiError: the base error class. Used for any errors which don't fall into the above groups; for instance, using a retired endpoint or no SSL. Currently covers:
    • GOTO_NEW_API (68)
    • SSL_REQUIRED (92)
    • THIS_ENDPOINT_HAS_BEEN_RETIRED (251)

I've also included some error codes not mentioned in the docs (as at the time of writing), but which I've personally received.

If you need to take action based on a specific type of error, I recommend checking the response code rather than relying on the inferred class.

Each error thrown by this package has the following properties:

  • code: The error code from the Twitter response. You can programmatically inspect the code and decide what specific action to take. For instance, for a rate limit error, to back off for a varying number of minutes depending on the error, we could do (from the last example):
     return aargh(e)
    .type(RateLimited, (e) => {
        if (e.code === codes.CANT_FOLLOW_MORE_PEOPLE_NOW_SO_CHILL) {
            retryIn({hours: 24});
        } else if (e.code === codes.HIT_TWEET_LIMIT) {
            retryIn({minutes: 30});
        }
    })
    .throw();

The various error codes are provided as properties of the codes object.

  • name: The name of the error class
  • errors: The original array of errors from the Twitter API response (as gotten from the response value passed to the wrapTwitterErrors function)
  • endpoint: The endpoint for the API call (the same value passed to the wrapTwitterErrors function)

Calling the valueOf() method of any of the error objects returns a nice JSON representation of the above properties ( useful for logging purposes).

About

Handle errors from Twitter API calls

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published