6

I am trying to send data from my node application to a 3rd party HTTP endpoint.

I am parsing the data on the request object coming from the client using the multiparty module and sending the data through the request module. I am getting the error

error: uncaughtException: source.on is not a function

var request = require('request');
const multiparty = require('multiparty');

function addAttachment(req, res) {
  let form = new multiparty.Form();
  let parsedFile = {};
  const formData = {};
  form.parse(req, function(err, fields, files){
    Object.keys(fields).forEach(function(name) {
      formData[name] = fields[name][0];
    });

    Object.keys(files).forEach(function(name) {
      logger.debug(name);
      parsedFile[name] = files[name][0];
    });
    formData.uploadFile = parsedFile.uploadFile;
    logger.debug('formData ', formData);

    reqOptions.url = imageURL;
    reqOptions.formData = formData;

    logger.debug('REQ_OPTIONS ', reqOptions);

    request.post(reqOptions, function (err, response, body) {
      if (err) {
        logger.warn(req, ' Error sending attachment', err);
        res.status(400);
        res.json({ "msg": "Error sending attachment" });
      } else {
        res.status(201);
        logger.debug('BODY ', body);
        res.send(body);
      }
    });
  });
}

The reqOptions obj contains the headers, url, auth obj, we then add the form data to it.

When I log the form data it looks to be in the correct format

{
"meta": {
    "prop1": "xxxxxx",
    "prop2": "xxxxxxxxxxxxx",
    "uploadFile": {
        "fieldName": "uploadFile",
        "originalFilename": "test.PNG",
        "path": "/tmp/W1IppPiK04JpkPrnZWEhzkmV.PNG",
        "headers": {
            "content-disposition": "form-data; name=\"uploadFile\"; filename=\"test.PNG\"",
            "content-type": "image/png"
        },
        "size": 42786
    }
}

}

1
  • the solution is working in you? Commented Oct 13, 2021 at 5:06

2 Answers 2

16

This error is probably one of the best examples how error message can be perfectly misleading. Therefore. it's very frustrating to do RCA of the issue:

ERROR: uncaught Exception: source.on is not a function

Actually there is nothing about any function here. In my case, I spent hours scratching my head and finally only to find it is JSON under another JSON which was causing this error:

  let subJson = 
  {
      field1: "value1",
      field2: "value2"
  }

  let myJson = 
  {
      field1: "value1",
      field2: "value2",
      field3: subJson
  }

  createFormData(myJson);

This is it! When you call createFormData with myJson as parameter, you will see exception source.on is not a function! And we keep thinking where is that function?

Solution is JSON.stringify

   field3: JSON.stringify(subJson)

Will solve this issue.

javascript!

Sign up to request clarification or add additional context in comments.

2 Comments

Cannot upvote this enough
That's not a JavaScript problem. createFormData, whatever that is, is doing something wrong.
0

So after some hair pulling and digging around, I was able to post form data to the external API. I decide to change the node modules I was using to connect-multiparty. Connect will parse the request headers and decode the post-form-data allowing you to access the data from the req obj E.G req.body now have the added properties and req.files has uploaded files.

const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();

Then add the multipartMiddleware to the route.

 app.post('/api/addAttachment' multipartMiddleware, MyController.addAttachment);

Then in my controller file I changed the code to use connect-multipart.

const fs = require('fs');
var request = require('request');  

function addAttachment(req, res) {
  const TMP = '/tmp';

  let formData = {};

  Object.keys(req.body).forEach((propName) =>{
   if (typeof propName === 'string') {
    logger.debug(propName, ' is a string');
    formData[propName] = req.body[propName];
   } else {
    logger.debug(propName, ' is not a string')
   }
  });
  //The files get added to the tmp folder on the files system,
  //So we create a stream to read from tmp folder, 
  //at the end end we need to delete the file
  formData['uploadFile'] = fs.createReadStream(req.files.uploadFile.path);
  logger.debug('FORM DATA ', formData, '\n');

  reqOptions.url = imageUrl;
  reqOptions.headers = {'Content-Type': 'multipart/form-data','Accept': 'application/json'};
  reqOptions.formData = formData;

  logger.debug('REQ_OPTIONS ', reqOptions, '\n');

  request.post(reqOptions, function (err, response, body) {
    if (err) {
      removeFiles(TMP);
      logger.warn(req, ' Error sending attachment', err);
      res.status(400);
      res.json({"msg": "Error sending attachment"});
    } else {
      removeFiles(TMP);
      res.status(201);
      logger.debug('BODY ', body);
      res.send(body);
    }
  });
}

3 Comments

It's not working in mine.
@user10046747 anymore details on what was not working?
But this solution works when you have backend code ready, I am trying to send request to another API where I cannot add multipart middleware in route, what should I do ?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.