Search code examples
twittertwitter4j

401 Auth error retrieving tweets


I have an app that runs daily and has been working for several months. It was using twitter4j v3.0.3.

Several weeks ago, it started to throw the common 401 exception:

401:Authentication credentials (https://dev.twitter.com/pages/auth) were missing or incorrect. Ensure that you have set valid consumer key/secret, access token/secret, and the system clock is in sync.
{"request":"\/1.1\/statuses\/user_timeline.json","error":"Not authorized."}

Relevant discussions can be found on the Internet at:
    http://www.google.co.jp/search?q=4be80492 or
    http://www.google.co.jp/search?q=0a6306df
TwitterException{exceptionCode=[4be80492-0a6306df], statusCode=401, message=null, code=-1, retryAfter=-1, rateLimitStatus=RateLimitStatusJSONImpl{remaining=112, limit=180, resetTimeInSeconds=1448156737, secondsUntilReset=428}, version=4.0.4}
    at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:164)
    at twitter4j.HttpClientBase.request(HttpClientBase.java:57)

I have updated to v4.0.4 (the latest at time of writing) & confirmed the correctness of all access and client tokens. I've also tried creating a brand new app and using those tokens. I've synchronised my system clock. All to no avail.

My source:

val config = new ConfigurationBuilder()
  .setOAuthConsumerKey(conf.getString("twitter.api.key"))
  .setOAuthConsumerSecret(conf.getString("twitter.api.secret"))
  .setOAuthAccessToken(conf.getString("twitter.access.token"))
  .setOAuthAccessTokenSecret(conf.getString("twitter.access.secret"))
  .build()

val twitter = new TwitterFactory(config).getInstance

def statuses(handle: String, since: Long): Stream[Status] = {
  @tailrec
  def loop(page: Int, acc: Stream[Status]): Stream[Status] = {
    val paging = new Paging(page, since)
    val xs = twitter.getUserTimeline(handle, paging).iterator().asScala.toSeq
    val stream = Stream.concat(acc, xs.filterNot(_.getText.startsWith("@")).filterNot(_.getText.startsWith("RT ")).toStream)
    if (xs.size < 20) stream
    else loop(page + 1, stream)
  }
  loop(1, Stream.empty)
}

It fails at twitter.getUserTimeline.

Why would this have failed when nothing changed in the code, the config or in my twitter account?

I've ruled out keys & clock sync. What more can I look into?

The entire project is available for testing at https://github.com/Synesso/tweet-mail (it needs some setup, as per the readme - but the smtp setup is probably not required).


Solution

  • (Converting my comment into an answer.)

    In order to connect securely to Twitter, you will need an up-to-date version of cacert.pem

    You can find this at https://github.com/jublonet/codebird-php/blob/develop/src/cacert.pem - that contains the latest certificate which will allow to you connect to Twitter.

    You can find details of Twitter's certificate at https://dev.twitter.com/overview/api/ssl

    Depending on your OS and programming language, you'll either need to put the file in a readable location or install it locally.