Last night I was playing with some code that looked sort of like this:

import http from 'axios';

const postMessage = (message) =>'/messages/add', { message }).catch(response =>;

postMessage('hello world').then(() => {
}).catch((e) => {

Essentially I had a helper function (postMessage) making an Ajax call using axios. This helper function was catching any errors and replacing the returned response object with a property of it, data. The idea was that any consumer of this helper function could then rely on the variable being sent in its catch to be the actual error message (rather than a complex object with the error message nested inside it somewhere), which it could then display to the user.

The problem was that even in the case of the server returning a non-200 status (e.g. a 400), the catch would never get triggered. Instead, the then block would get triggered. This seemed very strange to me. Why would a .then() occur on a non-200? Was this an axios issue? Surely not, I thought. I use axios extensively and I’ve never encountered this before. To troubleshoot, I abstracted the problem to be a pure JavaScript promise without axios involved.

const postMessage = (outcome) => new Promise((resolve, reject) => {
    if (outcome) {
    } else {

const postMessageWrapper = (outcome) => postMessage(outcome).catch((e) => e);

// This will log "Failure" to the console
postMessageWrapper(false).then((e) => console.log(e));

As you can see, even here, the promise is getting rejected but the .then() block is executing. The explanation is that the postMessageWrapper is obscurring the thrown error. The solution is to modify the postMessageWrapper so that it essentially re-throws the error, like so:

const postMessageWrapper = (outcome) => postMessage(outcome).catch((e) => Promise.reject(e));

The takeaway is this: if you catch something and you want a consumer of your API to also be able to catch it, you’ll need to Promise.reject() it.