Statuses/update_with_media: Error creating status 189


#1

When trying to post a tweet with media, the Twitter API returns an error code.

  • API version: 1.1
  • API method: POST statuses/update_with_media
  • Language used to access API: JavaScript

JavaScript has problems working with raw binary data within strings,
so the media file to send along with the tweet has been encoded in base64.

According to http://www.w3.org/Protocols/rfc1341/5_Content-Transfer-Encoding.html, the corresponding header field has been set in the MIME request.

Here is a complete dump of the sent request:

POST /1.1/statuses/update_with_media.json HTTP/1.1
Host: api.twitter.com
Connection: keep-alive
Content-Length: 64682
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36
Origin: http://local
Authorization: OAuth oauth_consumer_key="PNVfyHvoowa9h0Tt4fF3VQ", oauth_nonce="tgWeGMeH", oauth_signature="3%2BDGYLibvHRQ15q4jtqgj%2F%2FNzIw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1370095947", oauth_token="14648265-rPn8EJwfBG1FAzGmYUd1YxJB18LJwdEpzlNvEM8SZ", oauth_version="1.0"
Content-Type: multipart/form-data; boundary=--------------------sDUUB6pr
Accept: */*
Referer: http://local/dev/codebird-js/test-multipart.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: guest_id=v1%3A136179970190134552; __qca=P0-1800224371-1362343699183; ELOQUA=GUID=D7F1BCFF76114003909680A8A303706C; ELQSTATUS=OK; external_referer=vC8TI7P7q9VULaQby8GjGVMlOM6mdzuhVU%2ByWiXFBFM%3D%7C0; twll="l=1370087765"; remember_checked=0; __utma=43838368.2113840127.1361799708.1370087750.1370094443.51; __utmb=43838368.1.10.1370094443; __utmz=43838368.1370087750.50.16.utmcsr=api.twitter.com|utmccn=(referral)|utmcmd=referral|utmcct=/oauth/authorize; __utmv=43838368.lang%3A%20de; lang=de; twid=u%3D14648265%7ChLcHOslbn6Y9mryuGLojr4XgDw8%3D; _twitter_sess=BAh7CDoMY3NyZl9pZCIlMWEzY2U4NDA3MDhhNjJlZDFmODQ4ZjI3ODk4MjY4%250AZDk6B2lkIiU5ZDg1MTE0ZmFjMzg4NmM4NGM3ZDNiOWQxZDc4ZGQyODoPY3Jl%250AYXRlZF9hdGwrCKQvBgA%252FAQ%253D%253D--2f933caa70d01d991b43c517503f9668af293827

----------------------sDUUB6pr
Content-Disposition: form-data; name="status"

Somehow MySQL has updated their logo. But not like this:
----------------------sDUUB6pr
Content-Disposition: form-data; name="media[]"
Content-Transfer-Encoding: base64

/9j/4AAQSkZJRgABAQEBLAEsAAD/
--snip--
zmGP3rnSjiBdQywB6Ppch3Fs9xGrKkd7Tati4nmOABlB/6G/ooeeMttyV08477M3jDzHbvlY4ergU/1OMG3yMYe7XLN9amePM/5AKfDY/dwu+5V7XgpUvL/wDh/9k=
----------------------sDUUB6pr--

Here is a complete dump of the received response:

HTTP/1.1 403 Forbidden
cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-type: application/json; charset=utf-8
date: Sat, 01 Jun 2013 14:12:28 GMT
expires: Tue, 31 Mar 1981 05:00:00 GMT
last-modified: Sat, 01 Jun 2013 14:12:28 GMT
pragma: no-cache
server: tfe
set-cookie: dnt=; domain=.twitter.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
status: 403 Forbidden
strict-transport-security: max-age=631138519
x-access-level: read-write-directmessages
x-frame-options: SAMEORIGIN
x-mediaratelimit-class: photos
x-mediaratelimit-limit: 100
x-mediaratelimit-remaining: 98
x-mediaratelimit-reset: 1370164313
x-mid: 86821e1aa81553fb0a14208d3e582d639fb183fa
x-runtime: 0.16406
x-transaction: a777c36ac803515f
x-transaction-mask: a6183ffa5f8ca943ff1b53b5644ef114b76befca
Content-Length: 59

{"errors":[{"code":189,"message":"Error creating status"}]}

How can this issue be fixed?

Note: When sending the same request from PHP (and without base64 encoding), it succeeds. Take a look:

Successful request with PHP:

POST https://api.twitter.com/1.1/statuses/update_with_media.json HTTP/1.1
Host: api.twitter.com
Accept: */*
Authorization: OAuth oauth_consumer_key="PNVfyHvoowa9h0Tt4fF3VQ", oauth_nonce="e434108d", oauth_signature="OFw720YH4ne%2BphnDma%2F%2BnoM9eyA%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1370094420", oauth_token="14648265-rPn8EJwfBG1FAzGmYUd1YxJB18LJwdEpzlNvEM8SZ", oauth_version="1.0"
Content-Length: 48586
Content-Type: multipart/form-data; boundary=----------------------------1fc9651dad39

------------------------------1fc9651dad39
Content-Disposition: form-data; name="status"

Somehow MySQL has updated their logo. But not like this:
------------------------------1fc9651dad39
Content-Disposition: form-data; name="media[]"

BINARY_IMAGE_DATA_HERE_DELETED_FOR_FORUM_THREAD
------------------------------1fc9651dad39--

Successful response:

HTTP/1.1 200 OK
cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-length: 2946
content-type: application/json; charset=utf-8
date: Sat, 01 Jun 2013 13:47:04 GMT
etag: "5f7bf1e6378dd51473a840722e2409a9"
expires: Tue, 31 Mar 1981 05:00:00 GMT
last-modified: Sat, 01 Jun 2013 13:47:03 GMT
pragma: no-cache
server: tfe
set-cookie: dnt=; domain=.twitter.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: twid=u%3D14648265%7ChLcHOslbn6Y9mryuGLojr4XgDw8%3D; domain=.twitter.com; path=/; secure
set-cookie: _twitter_sess=BAh7CDoMY3NyZl9pZCIlZjM3MjllNTBmYjliZTYxZDhlN2NmNzRhZGY0ZDJh%250ANmU6B2lkIiU1ZWE4NzBlYjRhYmNkOGQxNDk1ZWFhZWUzYjc5MGQ5OToPY3Jl%250AYXRlZF9hdGwrCHXM%252Ff8%252BAQ%253D%253D--b5c590e38f120603503f0ac61065393ef9c50afe; domain=.twitter.com; path=/; HttpOnly
set-cookie: guest_id=v1%3A137009442277591021; Domain=.twitter.com; Path=/; Expires=Mon, 01-Jun-2015 13:47:04 UTC
status: 200 OK
strict-transport-security: max-age=631138519
x-access-level: read-write-directmessages
x-frame-options: SAMEORIGIN
x-mediaratelimit-class: photos
x-mediaratelimit-limit: 100
x-mediaratelimit-remaining: 98
x-mediaratelimit-reset: 1370164313
x-mid: 00414393be406ece7f0de904216581039c8790fe
x-runtime: 1.26835
x-transaction: 779ee46a92c25d37
x-transaction-mask: a6183ffa5f8ca943ff1b53b5644ef114b76befca

{"in_reply_to_status_id_str":null,"entities":{"user_mentions":[],"urls":[],"media":[{"media_url_https":"https:\/\/pbs.twimg.com\/media\/BLrcMjRCQAIi-_C.jpg","display_url":"pic.twitter.com\/9CQqwaR4M6","sizes":{"large":{"w":640,"h":427,"resize":"fit"},"small":{"w":340,"h":227,"resize":"fit"},"medium":{"w":600,"h":400,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"}},"expanded_url":"http:\/\/twitter.com\/myx\/status\/340826829998931970\/photo\/1","id":340826830003126274,"id_str":"340826830003126274","indices":[57,79],"source_status_id":null,"type":"photo","url":"http:\/\/t.co\/9CQqwaR4M6","media_url":"http:\/\/pbs.twimg.com\/media\/BLrcMjRCQAIi-_C.jpg"}],"hashtags":[]},"place":null,"in_reply_to_user_id":null,"possibly_sensitive":false,"geo":null,"retweet_count":0,"in_reply_to_user_id_str":null,"source":"\u003Ca href=\"http:\/\/mynetx.net\/\" rel=\"nofollow\"\u003Emynetx\u003C\/a\u003E","coordinates":null,"retweeted":false,"id_str":"340826829998931970","id":340826829998931970,"user":{"profile_background_tile":false,"name":"J.M.","verified":false,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/3005995399\/765485ad5721be61340ebbd0aa833564_normal.png","listed_count":56,"following":false,"profile_sidebar_fill_color":"B4D0EF","profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/3005995399\/765485ad5721be61340ebbd0aa833564_normal.png","url":"http:\/\/t.co\/aHcqQLzDXq","followers_count":2335,"contributors_enabled":false,"id":14648265,"location":"Germany","profile_background_color":"DEEAF8","description":"Microsoft expert. Coder. #IEuseragents. Web & app developer. Localization moderator for Twitter in German. I am my own brand.","screen_name":"myx","default_profile_image":false,"profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/662874303\/x8ctzxac0q9n8g3w516b.jpeg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/14648265\/1363160411","profile_link_color":"138CDD","utc_offset":3600,"time_zone":"Berlin","follow_request_sent":false,"favourites_count":164,"entities":{"description":{"urls":[]},"url":{"urls":[{"display_url":"mynetx.net","expanded_url":"http:\/\/mynetx.net\/","indices":[0,22],"url":"http:\/\/t.co\/aHcqQLzDXq"}]}},"id_str":"14648265","profile_use_background_image":true,"protected":false,"is_translator":true,"lang":"de","profile_text_color":"464646","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/662874303\/x8ctzxac0q9n8g3w516b.jpeg","notifications":false,"geo_enabled":true,"default_profile":false,"profile_sidebar_border_color":"FFFFFF","friends_count":87,"statuses_count":7489,"created_at":"Sun May 04 14:09:54 +0000 2008"},"in_reply_to_status_id":null,"created_at":"Sat Jun 01 13:47:04 +0000 2013","in_reply_to_screen_name":null,"truncated":false,"favorited":false,"contributors":null,"text":"Somehow MySQL has updated their logo. But not like this: http:\/\/t.co\/9CQqwaR4M6"}

#2

Both requests use the same signing heuristic? In that they both don’t sign the request body? The only difference between the two is that the Javascript version base64-encodes the body?

Would you be able to test base64-encoding the PHP request to make sure that this is the only differentiation?


#3

Modified codebird-php (!) to send a request with base64.
It looks like this:

POST /1.1/statuses/update_with_media.json HTTP/1.1
Host: api.twitter.com
Accept: /
Authorization: OAuth oauth_consumer_key=“PNVfyHvoowa9h0Tt4fF3VQ”, oauth_nonce=“e9931ead”, oauth_signature=“Q1tpSBHFvTl%2FCwOU3nsXSVxYEvU%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1371302972”, oauth_token=“14648265-rPn8EJwfBG1FAzGmYUd1YxJB18LJwdEpzlNvEM8SZ”, oauth_version="1.0"
Content-Length: 1039
Content-Type: multipart/form-data; boundary=--------------------4076de35

----------------------4076de35
Content-Disposition: form-data; name=“status”

Test tweet (PHP version).
----------------------4076de35
Content-Disposition: form-data; name="media[]"
Content-Transfer-Encoding: base64

iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB+0lEQVR42mP8//8/Ay0BEwONwagFoxZQDljI0PP8x7/Z93/e+PxXmpMpXp5dh4+ZgYHh0bd/clxYnMuINaMtfvRLgp3RVZwVU+rkuz+eRz+//wXVxcrEkKnEceXTX0dRlhoNTmKDaOvzXwHHv6x9+gtN/M9/hpjTX+GmMzAw/P7HMOnOj+ff//35x/Ds+z9iLfjPwPDt7//QE1/Sz319/RNh3PkPf+58+Yup/t7Xf9p8zFKcTMRa4CLGCrFm1v2fSjs+pJ/7uuvl7w+//yO7HRkUq3GEyrCREMk+kqy2IiyH3/xhYGD48uf/rPs/Z93/yczIwM3CiFU9Hw5xnD4ouvTt4Tf0AP37n+HTb+w+UOBmIs2CICm2R9/+EZlqGRkYzIVYSLMgRIYtUYGdSAsMBFgUuJhIy2iMDAwt2pysjAwLHv78RcgnOcrs5BQVHEyMG579Imi6Nh9zrBxZFgixMW624pXnwldYcTAzLjDhZmUit7AzE2K54c7fp8eF1QhWRobFptwmgiwkF3b//jMwMjJ8+P3/zPs/yx/9Wvr412+MgBJlZ1xsyuOOrbAibMHH3/87b32fce/nR2ypnpuFMVGevU6TQ5SdqKKeEVez5cuf/7te/j727s+9L/++/v3PzcyowM1kIcTiLs7Kz8pIfNnOONouGrVg1AIGAJ6gvN4J6V9GAAAAAElFTkSuQmCC
----------------------4076de35–

Twitter replied like this:

HTTP/1.1 403 Forbidden
cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
content-length: 59
content-type: application/json; charset=utf-8
date: Sat, 15 Jun 2013 13:31:40 GMT
expires: Tue, 31 Mar 1981 05:00:00 GMT
last-modified: Sat, 15 Jun 2013 13:31:40 GMT
pragma: no-cache
server: tfe
set-cookie: dnt=; domain=.twitter.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: lang=de; path=/
set-cookie: twid=u%3D14648265%7ChLcHOslbn6Y9mryuGLojr4XgDw8%3D; domain=.twitter.com; path=/; secure
set-cookie: _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCFTBCEg%252FAToHaWQiJWJiNTczNmNmMTM3Zjc4%250AOWJmYzc1ZmFlZTJmZGVkY2NiOgxjc3JmX2lkIiUyYTgxYzgzNzdlNzU5NDAz%250AM2E0NjBhZTUwNTNkYTExNw%253D%253D–04106207edd208bc92392b642bc8818205377fc7; domain=.twitter.com; path=/; HttpOnly
set-cookie: guest_id=v1%3A137130310057419027; Domain=.twitter.com; Path=/; Expires=Mon, 15-Jun-2015 13:31:41 UTC
status: 403 Forbidden
strict-transport-security: max-age=631138519
x-access-level: read-write-directmessages
x-frame-options: SAMEORIGIN
x-mediaratelimit-class: photos
x-mediaratelimit-limit: 100
x-mediaratelimit-remaining: 100
x-mediaratelimit-reset: 1371389500
x-mid: 31a4b5c84f56a6828a6807b027caa00bad738e2b
x-runtime: 0.17485
x-transaction: 9dd9cccffa15ff5e
x-transaction-mask: a6183ffa5f8ca943ff1b53b5644ef11421e83fa6

{“errors”:[{“code”:189,“message”:“Error creating status”}]}

Then the only change I made was comment the base64 lines in my code, like this:

/*
$multipart_request .=
"\r\nContent-Transfer-Encoding: base64";
$value = base64_encode($value);
*/

When resending the request with this code edit, Twitter created the status.
See here: https://twitter.com/myx/statuses/345897034068398080

So, the problem does not depend on the scripting language used, but instead the Twitter API 1.1 method “statuses/update_with_media” does not like media files that are transfer-encoded with base64.

This issue should be resolved since JavaScript cannot properly handle binary strings, which is why they need to be encoded.


#4

We just rolled out an update which supports this. Can you test again and confirm that the JS version works now?


#5

Track the progress of this feature here: https://github.com/mynetx/codebird-js/issues/46


#6

Success! Your update is successful, media uploads with base-64 encoded multipart are now accepted.

{ "created_at": "Sun Aug 25 19:12:23 +0000 2013", "id": 371711673885016064, "id_str": "371711673885016064", "text": "Ever been interested in seeing the full version of my avatar? Take a look! #wallpaper http:\/\/t.co\/dcKqIQvsm7", "source": "\u003ca href=\"http:\/\/mynetx.net\/\" rel=\"nofollow\"\u003emynetx\u003c\/a\u003e", "truncated": false, "in_reply_to_status_id": null, "in_reply_to_status_id_str": null, "in_reply_to_user_id": null, "in_reply_to_user_id_str": null, "in_reply_to_screen_name": null, "user": { "id": 14648265, "id_str": "14648265", "name": "J.M.", "screen_name": "myx", "location": "Germany", "description": "I am not @MYXphilippines! \r\n\u2014Microsoft expert. Coder. #IEuseragents. Web & app developer. Localization moderator for Twitter in German. I am my own brand.", "url": "http:\/\/t.co\/aHcqQLzDXq", "entities": { "url": { "urls": [{ "url": "http:\/\/t.co\/aHcqQLzDXq", "expanded_url": "http:\/\/mynetx.net\/", "display_url": "mynetx.net", "indices": [0, 22] }] }, "description": { "urls": [] } }, "protected": false, "followers_count": 2447, "friends_count": 104, "listed_count": 60, "created_at": "Sun May 04 14:09:54 +0000 2008", "favourites_count": 174, "utc_offset": 7200, "time_zone": "Berlin", "geo_enabled": true, "verified": false, "statuses_count": 7724, "lang": "de", "contributors_enabled": false, "is_translator": true, "profile_background_color": "DEEAF8", "profile_background_image_url": "http:\/\/a0.twimg.com\/profile_background_images\/662874303\/x8ctzxac0q9n8g3w516b.jpeg", "profile_background_image_url_https": "https:\/\/si0.twimg.com\/profile_background_images\/662874303\/x8ctzxac0q9n8g3w516b.jpeg", "profile_background_tile": false, "profile_image_url": "http:\/\/a0.twimg.com\/profile_images\/3005995399\/765485ad5721be61340ebbd0aa833564_normal.png", "profile_image_url_https": "https:\/\/si0.twimg.com\/profile_images\/3005995399\/765485ad5721be61340ebbd0aa833564_normal.png", "profile_banner_url": "https:\/\/pbs.twimg.com\/profile_banners\/14648265\/1372249396", "profile_link_color": "138CDD", "profile_sidebar_border_color": "FFFFFF", "profile_sidebar_fill_color": "B4D0EF", "profile_text_color": "464646", "profile_use_background_image": true, "default_profile": false, "default_profile_image": false, "following": false, "follow_request_sent": false, "notifications": false }, "geo": null, "coordinates": null, "place": null, "contributors": null, "retweet_count": 0, "favorite_count": 0, "entities": { "hashtags": [{ "text": "wallpaper", "indices": [75, 85] }], "symbols": [], "urls": [], "user_mentions": [], "media": [{ "id": 371711673759191042, "id_str": "371711673759191042", "indices": [86, 108], "media_url": "http:\/\/pbs.twimg.com\/media\/BSiVzEFIUAIZHLX.jpg", "media_url_https": "https:\/\/pbs.twimg.com\/media\/BSiVzEFIUAIZHLX.jpg", "url": "http:\/\/t.co\/dcKqIQvsm7", "display_url": "pic.twitter.com\/dcKqIQvsm7", "expanded_url": "http:\/\/twitter.com\/myx\/status\/371711673885016064\/photo\/1", "type": "photo", "sizes": { "small": { "w": 340, "h": 212, "resize": "fit" }, "large": { "w": 1024, "h": 640, "resize": "fit" }, "thumb": { "w": 150, "h": 150, "resize": "crop" }, "medium": { "w": 600, "h": 375, "resize": "fit" } } }] }, "favorited": false, "retweeted": false, "possibly_sensitive": false, "lang": "en" }