I am trying to console.log some data for a weather API, but when I go look up a location I get the error message
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
My code so far is this on my server
app.post('/weather', (req, res) => {
const url = `https://api.darksky.net/forecast/${DARKSKY_API_KEY}/${req.body.latitude},${req.body.longitude}?units=auto`
axios({
url: url,
responseType: 'json'
}).then(data => res.json(data.data.currently)).catch(error => { throw error})
})
app.listen(3000, () => {
console.log("Server has started")
})
and my Javascript
fetch('/weather', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
latitude: latitude,
longitude: longitude
})
}).then(res => res.json()).then(data => {
console.log(data)
setWeatherData(data, place.formatted_address)
}).catch(error => { throw error})
})
Your code:
axios(...).then(...).catch(error => { throw error;})
will lead to that warning if your axios() call or the .then() handler has a rejection.
When you throw the error in your .catch() handler that leaves the promise chain in a rejected state and you have no further code to catch that rejection.
The exact same issue is true of your client-side code.
You should also understand that .catch(error => { throw error;}) does absolutely nothing useful. It catches the rejection, then throws which just rejects the chain again. And, since there is nothing else listening to the promise chain, it's an unhandled rejection.
What you need to do instead is to actually handle the error in some way that is appropriate for your application such as send an error status back to the client.
axios(...).then(...).catch(error => {
console.log(err);
res.sendStatus(500);
});
And, in the client, you can either show the user an error message or just log the error. rethrowing the error if there's nobody listening to catch it does you no good.
axios({
url: url,
responseType: 'json'
}).then(data => res.json(data.data.currently)).catch(error => { console.error(error)})
I did the same thing when I started. :)
You wouldn't throw a baseball from the catcher's mitt, so you shouldn't throw an error from a catch method either. You can however throw from the then method and it will be caught by the following catch method.
Anyway, you should handle the error in the catch method. In most cases the error will be sent to that catch method automatically if it's a real error. If it's an error hidden inside a successful API request, you'd have to manually throw the error like I mentioned above.
.catch(error => yourCustomErrorHandlerThatShouldBeAvailableGloballyAndDefinitelyNotHaveANameThisLong(error))
Your custom error handler can do whatever you want. You can forward it to a service you're using to track crashes, you can display something to page visitors, you can print it to the console, make the page spin around really fast and explode, create an animated cat rainstorm, redirect to Darth Vader yelling "NOOOOOOO!". Sky's the limit.
Related
I was trying to make a get request to the spotify API using axios in node.js. But, I always end up getting a 400 bad request. Could someone please help me out? The code snippet and the error are pasted below.
app.get('/api/search', async (req, res) => {
const spotify_search_one = await axios.get('https://api.spotify.com/v1/search', {
headers: {
'Authorization': keys.spotifyClientId
},
params: {
q: "face",
type: "track"
}
});
console.log(spotify_search_one);
})
The error is as follows
UnhandledPromiseRejectionWarning: Error: Request failed with status code 400
[0] at createError (/Users/uddhavbhagat/Desktop/Projects/TuneIn/node_modules/axios/lib/core/createError.js:16:15)
[0] at settle (/Users/uddhavbhagat/Desktop/Projects/TuneIn/node_modules/axios/lib/core/settle.js:17:12)
[0] at IncomingMessage.handleStreamEnd (/Users/uddhavbhagat/Desktop/Projects/TuneIn/node_modules/axios/lib/adapters/http.js:236:11)
[0] at IncomingMessage.emit (events.js:322:22)
[0] at endReadableNT (_stream_readable.js:1187:12)
[0] at processTicksAndRejections (internal/process/task_queues.js:84:21)
[0] (node:12697) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 4)
The error you‘re getting is that the Promise (axios.get) does not have a catch.
Therefore when the api call results in an error, you are not handling it in any way. What about trying it the asynchronous way?
I would send the api call axios.get(‘URL‘, ...) and then handle the response with .then and .catch.
I would suggest the following:
axios.get('https://api.spotify.com/v1/search', { headers: { 'Authorization': keys.spotifyClientId } })
.then(response => {
const spotify_search_one = response.data;
}).catch(err => {
console.error(err);
})
This will ask the Spotify-API for the desired data and then execute anything you write in the .then when the API-call was successful. The variable response contains the data (in this case the search results) which you can get with response.data.
If the API-call fails everything in .catch will be called. You could print the error or handle it in any other way then.
I wrote a Telegram bot using TelegrafJS, this framework wraps the Telegram API. The problem that I'm facing is how to correctly manage the unhandled rejection, in fact when I call this method:
await ctx.deleteMessage(message_id);
where ctx is the instance of TelegrafJS I got:
Bot error: Error: 400: Bad Request: message to delete not found
this error happens 'cause the message_id that I passed no longer exists in Telegram chat. Now, the problem is that I have several controllers which can cause that problem.
I was looking at Promise.prototype.catch(), my question is: can I set up a global rejection handler for my application, or should I use a try/catch block to methods potentially subject to exceptions?
Yes you can, and it is pretty simple:
process.on('unhandledRejection', (err) => {
//handle it!
});
You can also catch the unhandled exceptions, using the same code basically:
process.on('uncaughtException', (err) => {
//handle it!
});
test this code:
bot.use((ctx, next) => {
try {
...
} catch(error) {
next();
}
})
For error handler, you can use:
bot.catch((err, ctx) => {
console.log(err);
return ctx.reply("Error Message");
});
I've been working on a new application that uses PostgreSQL and Knexjs as a query builder, but have been running into an issue I'm not sure how to handle.
I have a route handler that looks like so:
export const getSingleUser = async(req, res) => {
let respObj = {
status: 'fail',
message: 'User does not exist'
};
try {
const user = await knex('users').where('id', req.params.id).first();
if (!user) {
res.send(respObj);
}
respObj = {
status: 'success',
data: user
};
res.send(respObj);
} catch(e) {
res.send(respObj);
}
};
It works great, until I throw a non-existent user ID into the mix. I assumed the catch statement would handle the error if no user is found for the query, but that doesn't seem to work, it just spits out the respObj in the try block. So I added an if statement to check if the user object doesn't exist, and thats when I received the warning below:
(node:25711) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at validateHeader (_http_outgoing.js:503:11)
at ServerResponse.setHeader (_http_outgoing.js:510:3)
at ServerResponse.header (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:767:10)
at ServerResponse.send (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/express/lib/response.js:158:21)
at _callee3$ (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:45:7)
at tryCatch (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:65:40)
at Generator.invoke [as _invoke] (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:303:22)
at Generator.prototype.(anonymous function) [as next] (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/node_modules/regenerator-runtime/runtime.js:117:21)
at step (/Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:14:191)
at /Users/munsterberg/Sites/fullstack_workspace/esports-manager/services/project/src/controllers/userController.js:14:361
at <anonymous>
(node:25711) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25711) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Can anyone provide more info on why this is happening, and whats the fix?
Work around
A response is sent twice in the try block if the user does not exist. This accounts for the error raised:
"Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client".
The unhandled promise rejection warning is being raised because if the res.send() call in the catch code block throws, the promise returned from calling getSingleUser gets rejected with the same error - implying there is no error handling in place for the returned promise (because isn't supposed to get rejected).
Directing the "user does not exist" case to the catch block, by throwing an error, could be a work around to avoid the issue in the first place. A cut-down example:
export const getSingleUser = async(req, res) => {
try {
const user = await knex('users').where('id', req.params.id).first();
if (!user) {
throw new Error(" user does not exist");
}
res.send( {
status: 'success',
data: user
});
} catch(e) {
// console.log(e); // debug if needed
res.send( {
status: 'fail',
message: 'User does not exist'
});
}
};
I am using isomorphic-fetch to perform AJAX requests from my react-redux application. In my api middleware I have the following function which calls the external resource:
import fetch from 'isomorphic-fetch';
function callApi({ endpoint, method, body, params = {} }) {
let route = generateRoute(endpoint, params);
return fetch(route, generateFetchOptions(method, body))
.then(response => {
if (!response.ok) {
return Promise.reject(response);
}
return response.json();
});
}
The above function is called by the following piece of code:
return callApi(callAPI).then(
response => next(actionWith({
response,
type: successType,
statusCode: 200
})),
error => error.json().then(errorObject => {
return next(actionWith({
type: failureType,
statusCode: errorObject.statusCode,
error: errorObject.message || 'Something bad happened'
}));
})
);
If I reject with Promise.reject(response) the error is being handled by the error handler, but for some reason the error also bubbles to the browser console (in my case Chrome).
Here is a screenshot from the console which shows what is happening (api.js:34 is the second line of the callApi method):
This is the usual behavior (in probably every browser?) when hitting an error during an HTTP request (no matter whether a linked image cannot be found, or an XHR fails). No matter if and how you handle those errors, they will always be logged to the console. There is no way to suppress this behavior.
References:
Provide a way not to display 404 XHR errors in console
How can I stop jQuery.ajax() from logging failures to the console?
fields is undefined in the following code snipped, but it is not logged to the console when the error happens. In this specific instance, why, and what is the de facto way to handle this?
"Testing" is logged to the console (Line #2), but the undefined variable fields (Line #4) is not being reported. The error is returned in an API response (Line #5) but with no relevant information such as line #, stack trace, etc.
How can I make errors like this log to the console, and why are they not?
export function post(req, res) {
console.log("Testing")
User.create( getFields(req, ["name_first", "name_last"]) )
.then(user => respondJSON (res, fields, { status: 201 }))
.catch(err => respondError (res, err))
}
Since the catch is responding with an error, I get the following API response:
{
"error": true,
"data": {
"message": "fields is not defined"
}
}
I am using Babel 6 and babel-node to run my code through NPM scripts. I am using morgan logging as well. Removing the middleware for logging does not alter the error output.
The automatic logging to console is a mechanism for unhandled exceptions. Because Promises automatically catch exceptions in the callbacks, the exceptions are no-longer unhandled, so nothing will be automatically logged.
If you want it to be logged, you could perhaps add a throw err at the end of your catch block. This will convert it into an unhandled promise rejection, which is typically handled similarly to an unhandled exception.
Because you didn't actually log the error?
export function post(req, res) {
console.log("Testing")
User.create( getFields(req, ["name_first", "name_last"]) )
.then(user => respondJSON (res, fields, { status: 201 }))
.catch(err => {
console.error(err);
respondError(res, err);
});
}
I had a similar problem caused by a 'finally' which was appended to the main async function running.
run()
.finally(()=>{process.exit(0)})
modifying it to:
run()
.catch(err => {console.log(err)})
.finally(()=>{process.exit(0)})
solved the problem