Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: Meteor code must always run within a Fiber

Tags:

meteor

I am using stripe for payments in my app, I want to create a receipt document in my own database after a succesful transaction

My code:

Meteor.methods({
  makePurchase: function(tabId, token) {
    check(tabId, String);
    tab = Tabs.findOne(tabId);

    Stripe.charges.create({
      amount: tab.price,
      currency: "USD",
      card: token.id
    }, function (error, result) {
      console.log(result);
      if (error) {
        console.log('makePurchaseError: ' + error);
        return error;
      }

      Purchases.insert({
        sellerId: tab.userId,
        tabId: tab._id,
        price: tab.price
      }, function(error, result) {
        if (error) {
          console.log('InsertionError: ' + error);
          return error;
        }
      });
    });
  }
});

However this code returns an error:

Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

I am not familiar with Fibers, any idea as to why this is?

like image 496
Tarlen Avatar asked Jan 04 '15 19:01

Tarlen


2 Answers

The problem here is that the callback function which you pass to Stripe.charges.create is called asynchronously (of course), so it's happening outside the current Meteor's Fiber.

One way to fix that is to create your own Fiber, but the easiest thing you can do is to wrap the callback with Meteor.bindEnvironment, so basically

Stripe.charges.create({
  // ...
}, Meteor.bindEnvironment(function (error, result) {
  // ...
}));

Edit

As suggested in the other answer, another and probably better pattern to follow here is using Meteor.wrapAsync helper method (see docs), which basically allows you to turn any asynchronous method into a function that is fiber aware and can be used synchronously.

In your specific case an equivalent solution would be to write:

let result;
try {
  result = Meteor.wrapAsync(Stripe.charges.create, Stripe.charges)({ /* ... */ });
} catch(error) {
  // ...
}

Please note the second argument passed to Meteor.wrapAsync. It is there to make sure that the original Stripe.charges.create will receive the proper this context, just in case it's needed.

like image 91
Tomasz Lenarcik Avatar answered Nov 09 '22 15:11

Tomasz Lenarcik


You might want to take a look at the docs for http://docs.meteor.com/#/full/meteor_wrapasync.

like image 39
Chip Castle Avatar answered Nov 09 '22 17:11

Chip Castle