That dreaded 401 Error again


#1

I’ve been using a very simple C# function to post alerts to my community on Twitter. Has worked well over the past few years until last Friday. Now suddenly the very same code is intermittently throwing 401 errors. The behavior I’m seeing is that I run a very simple test in VisualStudio and it works fine with a test string like ‘This is a Twitter API test.’

Then I start adding a URL from tinyurl or a few more words and it suddenly triggers the 401. I made sure everything is encoded properly - please see my code which is posted below. By the way I did recreate all my tokens and even created a new app in Application Management, but to no avail. It works for one or two tests with very short updates, but as soon as I include anything of substance (e.g. a URL or a few more words) it’ll start throwing this error. Once again, I have used this code for years for literally thousands of tweets including URLs and various hashtags, etc. etc. - never failed me. All this trouble started last Friday. Has my account been flagged or something?

Any help would be appreciated as I’m pulling my hair out over this. Here’s my code:

private String PostMessageToTwitter(string message)
        {
            string facebookURL = "https://api.twitter.com/1.1/statuses/update.json";

            //set the access tokens (REQUIRED)
            string oauth_consumer_key = "xxxx";
            string oauth_consumer_secret = "xxxx";
            string oauth_token = "xxx-xxx";
            string oauth_token_secret = "xxxxx";


            // set the oauth version and signature method
            string oauth_version = "1.0";
            string oauth_signature_method = "HMAC-SHA1";

            // create unique request details
            string oauth_nonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(DateTime.Now.Ticks.ToString()));
            System.TimeSpan timeSpan = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
            string oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();

            // create oauth signature
            string baseFormat = "oauth_consumer_key={0}&oauth_nonce={1}&oauth_signature_method={2}" + "&oauth_timestamp={3}&oauth_token={4}&oauth_version={5}&status={6}";

            string baseString = string.Format(
                baseFormat,
                oauth_consumer_key,
                oauth_nonce,
                oauth_signature_method,
                oauth_timestamp, oauth_token,
                oauth_version,
                Uri.EscapeDataString(message)
            );

            string oauth_signature = null;
            using (HMACSHA1 hasher = new HMACSHA1(ASCIIEncoding.ASCII.GetBytes(Uri.EscapeDataString(oauth_consumer_secret) + "&" + Uri.EscapeDataString(oauth_token_secret))))
            {
                oauth_signature = Convert.ToBase64String(hasher.ComputeHash(ASCIIEncoding.ASCII.GetBytes("POST&" + Uri.EscapeDataString(facebookURL) + "&" + Uri.EscapeDataString(baseString))));
            }

            // create the request header
            string authorizationFormat = "OAuth oauth_consumer_key=\"{0}\", oauth_nonce=\"{1}\", " + "oauth_signature=\"{2}\", oauth_signature_method=\"{3}\", " + "oauth_timestamp=\"{4}\", oauth_token=\"{5}\", " + "oauth_version=\"{6}\"";

            string authorizationHeader = string.Format(
                authorizationFormat,
                Uri.EscapeDataString(oauth_consumer_key),
                Uri.EscapeDataString(oauth_nonce),
                Uri.EscapeDataString(oauth_signature),
                Uri.EscapeDataString(oauth_signature_method),
                Uri.EscapeDataString(oauth_timestamp),
                Uri.EscapeDataString(oauth_token),
                Uri.EscapeDataString(oauth_version)
            );

            HttpWebRequest objHttpWebRequest = (HttpWebRequest)WebRequest.Create(facebookURL);
            objHttpWebRequest.Headers.Add("Authorization", authorizationHeader);
            objHttpWebRequest.Method = "POST";
            objHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
            using (Stream objStream = objHttpWebRequest.GetRequestStream())
            {
                byte[] content = ASCIIEncoding.ASCII.GetBytes("status=" + Uri.EscapeDataString(message));
                objStream.Write(content, 0, content.Length);
            }

            var responseResult = "";
            try
            {
                //success posting
                WebResponse objWebResponse = objHttpWebRequest.GetResponse();
                StreamReader objStreamReader = new StreamReader(objWebResponse.GetResponseStream());
                responseResult = objStreamReader.ReadToEnd().ToString();
            }
            catch (Exception ex)
            {
                //throw exception error
                responseResult = "Twitter Post Error: " + ex.Message.ToString() + ", authHeader: " + authorizationHeader;
            }
            return responseResult;
        }
    }

#2

I can imagine this is frustrating, sorry about that.

When you get the 401 error, what is the accompanying API response message?

The fact that this is intermittent would suggest that it’s not an account or app issue but maybe something else is going on. There’s a chance that our antispam system is treating Tweets in a particular format as problematic. It’s hard to say without know a little more about the app.


#3

Hey Andy - that was fast! Thanks so much for getting back to me so quickly. Alright - I just ran the test again and this is the full response string:

Response: Twitter Post Error: The remote server returned an error: (401) Unautho rized., authHeader: OAuth oauth_consumer_key="nXxxxxxxxxxxhg", oauth_ nonce="NxxxxxxxxxxDAw", oauth_signature="5dxxxxxxxxxx3 D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1470747998", oauth_toke n="632xxxxxxxxxxrQ0", oauth_version="1.0"

I don’t see anything in there that may provide a clue as to why the call is failing.

Here’s how I’m calling my test method in main();

Test test = new Test(); String response = Twitter.Twitter.PostMessage("TEST IGNORE! ES.20.Min Alert: http://tinyurl.com/hfpus4s Odds of ES closing HIGHER in 40 minutes (2 candles): 3.6 : 1.:"); Console.WriteLine("Response: " + response); Console.ReadLine();

That is literally it - is there anything here that strikes you as incorrect?


#4

Quick update - I just fixed a bug in some of my other messaging code that suggests that possibly too many Twitter messages may have been sent in the recent past. I can’t be sure but would it be possible for you guys to verify if my account has been flagged? Should I post my twitter handle in the open or can I email or message you privately?


#5

Unfortunately we’re not able to help with anything account related here - those issues are dealt with by a completely separate team and there are obviously privacy issues involved too. You’re free to use support.twitter.com to request account help, but I don’t know if they can help in terms of account “flagging”.

I’m curious if this happens if you don’t use a tinyurl - I’m just wondering if there’s something to do with that which might trigger this behaviour.


#6

Don’t give up on me yet!! :wink:

To answer your question: It doesn’t happen if I do not include a tinyurl link in the tweet (as mentioned). So apparently it is content related. And once I get the 401 I even get another if I try a tweet without a link. Then I wait like 30 minutes or so and I can run the test successfully again WITHOUT the link. But as soon as the link is added it’s 401 time - no matter what gets sent up afterward.


#7

Hey guys - could you please take another look at this? I don’t know how to fix this issue…


#8

Have you tried any other link shorteners? The URL will be shortened by our t.co handler anyway, so even including a target URL should work fine without the need for tinyurl.


#9

Okay, I will try that. The reason I used tinyurl is because it has an API that allows you to be called. The URLs I’m posting are Google charts (which are LOONG) and I need to convert them into short links in my C# code. This is my work flow:

  1. Retrieve chart data in my DB.
  2. Convert data into Google chart (example).
  3. Shorten URL (now done via calling api.tinyurl.com)
  4. Assemble Tweet content.
  5. Pass tweet content to PostMessageToTwitter()

Is there a way for me to call t.co beforehand in step 3 and retrieve a link I can use in my tweet?

Thank you in advance for taking the time to address this issue.


#10

I’m not knocking tinyurl - but I’m trying to help workaround the issue :slight_smile: - it’s annoying that this is happening and I can’t say why that URL format is causing problems.

There’s no API for t.co, but you should be able to post the long Google chart URL in a Tweet and have it automatically get converted by t.co in the resulting Tweet text, without the longer URL counting against your 140 chars. You might want to try that manually by pasting a long chart URL into a Tweet composer on the web first, to prove it to yourself.


#11

I know you weren’t knocking tinyurl - no worries. And having been in the engineering racket for well over two decades I completely understand that certain APIs or tools just sync better than others.

To be honest - it never occurred to me to just pass on the entire URL without shortening it first. At the danger of testing your patience: Where do I find a tweet composer? :blush:


#12

Okay, so I just ran my first test for the day with just “TEST IGNORE ES.20.Min…” and it immediately threw the 401:

Response: Twitter Post Error: The remote server returned an error: (401) Unautho
rized., authHeader: OAuth oauth_consumer_key=“nxxxxxxxxxxxhg”, oauth_
nonce=“NxxxxxxxxxxxDAw”, oauth_signature=“GTxxxxxxxxxxx9PI%3
D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1470824814”, oauth_toke
n=“632xxxxxxxxxxxurQ0”, oauth_version=“1.0”

So no embedded URL, nothing of the sort.

You’ve got the my code (shared above). I am happy to share my credentials with you or a member of your staff so you can run the test on your end. I’m at my wits end - not clue as to why the Twitter REST API has stopped working for me after years of using it without problems.


#13

Sorry “Tweet composer” is just the Tweet box on the twitter.com web page, or in the mobile app. Jargon!

Baffled myself. I’ll have a play when I can and see if there’s anything else I can figure out based on your code, but will need to get myself a C# environment to try it out.


#14

I recommend you simply run VisualStudio - the free edition. I use 2012 as it’s a bit faster/thinner than the latest one. Alternatively I recommend SharpDevelop which is excellent, fast, and of course free.

I can zip up the entire project and then create a custom app in Twitter with brand new authentication keys.

Please let me know when you’re ready to roll and I’ll email or upload the project for you.


#15

Hi again - it’s been a few days since your last response. Are you set up to run my code on your end?


#16

Sorry, I’ve not had time to dig into this yet :-/ will try to take a look this week.


#17

Have you verified that the oauth_timestamp is actually correct?
In the sense that is your machine actually at the correct datetime and region?


#18

TweetInvlApi: Good thinking and yes - affirmative. I did have it wrong on my test machine for a while but then corrected it. My production machine most definitely has it set correctly as it’s a trading application. But it simply keeps throwing this error without any further explanation. At this point Twitter is simply broken for me and I will have to consider a different platform for alert delivery.


#19

To be totally honest I cannot find out what is wrong with your code by taking a glance at it.
Have you tried the library that I have developed for c# called Tweetinvi?

I am not asking you to use it but to verify if it works for you with your credentials and your environment.
If it works you can then compare what is sent by Tweetinvi to what you actually send to Twitter.


#20

TwetInviApi - I may try that actually, thanks for the suggestion. FYI, I mentioned a bit higher in the thread that my code had worked for 2 years without any issues. So this is a big mystery to me as well :weary: