Search code examples
node.jstwitter

Getting HTTP Error: 400 Bad Request on GIF or video upload on Twitter


I am using Twitter npm package to make a simple app that can upload gif or video to the platform. Getting 400 Bad Request error on the FINALIZE step. I can't understand why am I getting this error. I have structured my code as the example provided by the package and the Twitter docs. There is not much discussion about this error on stack overflow and in Twitter forums, they are saying that if a media file doesn't pass validation then this error is produced but they didn't specify the validations. If someone can explain why is it happening then that would be a great help.

let fs = require('fs')
let path = require('path')

const keys = require('./config')

const client = new Twitter(keys)

let mediaIdString = ''
let mediaId = 0

//  reading file (GIF)
const gifFile = fs.readFileSync(path.join(__dirname, 'memes', 'gif1.gif'))

//  getting file details
const gifFileStat = fs.statSync(path.join(__dirname, 'memes', 'gif1.gif'))
const gifSize = gifFileStat.size
const contentType = 'image/gif'

console.log(`gif size in bytes ==> ${gifSize}`)

//  calling uploadMedia
uploadMedia().then((result) => {
    console.log('UPLOAD SUCCESSFUL')
}).catch((err) => {
    console.log(err)
})

function uploadMedia () {
    //    STEP 1 (INIT)
    return client.post('media/upload', {
        command: 'INIT',
        total_bytes: gifSize,
        media_type: contentType
    }).then((response) => {
        console.log(`INIT \t response ==> ${JSON.stringify(response)}`)

        mediaIdString = response.media_id_string
        mediaId = response.media_id

        console.log(`mediaId ==> ${mediaId}`)

        console.log(`media id string ==> ${mediaIdString}`)

        //  step 2 (APPEND)
        return client.post('media/upload', {
            command: 'APPEND',
            media_id: mediaIdString,
            media_data: gifFile,
            segment_index: 0
        })
    }).then((response) => {
        console.log(`APPEND \t response ==> ${JSON.stringify(response)}`)

        //  STEP 3 (STATUS)
        return client.get('media/upload', {
            command: 'STATUS',
            media_id: mediaIdString
        })
    }).then((response) => {
        console.log(`STATUS \t response ==> ${response}`)

        //  STEP 4 (FINALIZE)
        return client.post('media/upload', {
            command: 'FINALIZE',
            media_id: mediaIdString
        })
    })
}

Solution

  • The first issue is that causing you to get Error: HTTP Error: 400 Bad Request is the data you get by fs.readFileSync is a raw binary file rather than base64-encoded file. API reference of Twitter states that:

    media : The raw binary file content being uploaded. Cannot be used with media_data

    media_data : The base64-encoded file content being uploaded. Cannot be used with media

    POST media/upload > Parameters

    So you should use media parameter to pass the file instead of media_data.

    The second issue that your promise chain while trying to use chunked-upload approach is a little bit problematic and it should actually look like this:

    client.post('media/upload', {
      command: 'INIT',
      total_bytes: gifSize,
      media_type: contentType
    }).then((response) => {
      mediaIdString = response.media_id_string;
      client.post('media/upload', {
        command: 'APPEND',
        media_id: mediaIdString,
        media: gifFile,
        segment_index: 0
      }).then((response) => {
        client.post('media/upload', {
          command: 'FINALIZE',
          media_id: mediaIdString,
        }).then((response) => {
          console.log(response);
          console.log('UPLOAD SUCCESSFUL');
        });
      });
    });
    

    I think you tried to write a promise waterfall but I believe it does not work quite well in this case or if it works then it requires even more code to write and there is no performance difference at all.

    Additionally, there is no mention of STATUS command in twitter package's README.md so it seems like STATUS is not supported by this library.