How to add error handling to all node-red requests

Hi there,

I have an app where we fire off http requests to a node-red server. Some of the node-red flows/endpoints we've made and some are user created. As expected there are sometimes unexpected exceptions that occur within a flow. We can see the exception logged from the node-red server, but the http request that started the flow doesn't actually receive the error.

I am in search of a uniform way to catch any errors, including in user created endpoints, and respond to the request that initially hit the node-red server with the appropriate error.

This seems like an obvious piece of functionality to support, but it currently seems like every single one of the node-red http-in endpoints needs to have its own catch node set up for all the functions within that request and then properly respond to the appropriate http-out node. There must be a better way..?

Someone please help me find that better way. Thanks!

tags - global error handling middleware catch all error

You don't need to send the response to a specific http-out , any will do, do long as the msg has msg.req and msg.req from the original request.

@Steve-Mcl does a general catch node receive the msg.req/res?

Easily determined with a simple flow.

In short, yes - you can evaluate the response and throw an error using node.error(msg.error || 'unknown error', msg) in a function node - which can be caught by the catch node.

[{"id":"89f2abb8fcce392f","type":"inject","z":"8a88fee08ad63069","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1580,"y":120,"wires":[["da0852b8478a5b09"]]},{"id":"d3f32dbd14a6a11f","type":"catch","z":"8a88fee08ad63069","name":"","scope":null,"uncaught":false,"x":1880,"y":300,"wires":[["d7e3e99d341c20d3","e5c713c8408199c6"]]},{"id":"e9b9ba2501d898f8","type":"http in","z":"8a88fee08ad63069","name":"","url":"/bad","method":"get","upload":false,"swaggerDoc":"","x":1640,"y":240,"wires":[["5bd460a147aedc73"]]},{"id":"f63f1878ae70a158","type":"http response","z":"8a88fee08ad63069","name":"","statusCode":"","headers":{},"x":2070,"y":240,"wires":[]},{"id":"5bd460a147aedc73","type":"function","z":"8a88fee08ad63069","name":"throws error","func":"node.error(\"something bad happened\", msg);\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1870,"y":240,"wires":[["f63f1878ae70a158"]]},{"id":"68e8d3e6d7e0331f","type":"http in","z":"8a88fee08ad63069","name":"","url":"/good","method":"get","upload":false,"swaggerDoc":"","x":1640,"y":200,"wires":[["d493e6c6385d66ad"]]},{"id":"d493e6c6385d66ad","type":"function","z":"8a88fee08ad63069","name":"all good","func":"msg.payload = {data:'all good'}\nreturn msg\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1860,"y":200,"wires":[["af78f4616c9268c5"]]},{"id":"af78f4616c9268c5","type":"http response","z":"8a88fee08ad63069","name":"","statusCode":"","headers":{},"x":2070,"y":200,"wires":[]},{"id":"d4feaa554481a690","type":"inject","z":"8a88fee08ad63069","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1580,"y":80,"wires":[["b382327418826974"]]},{"id":"b382327418826974","type":"http request","z":"8a88fee08ad63069","name":"http://192.168.86.130:12080/bad","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://192.168.86.130:12080/good","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1830,"y":80,"wires":[["2fa80a7bce8ac0f8"]]},{"id":"2fa80a7bce8ac0f8","type":"debug","z":"8a88fee08ad63069","name":"debug 4","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2060,"y":80,"wires":[]},{"id":"d7e3e99d341c20d3","type":"debug","z":"8a88fee08ad63069","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2060,"y":300,"wires":[]},{"id":"2749c38a5d2bc8e3","type":"debug","z":"8a88fee08ad63069","name":"debug 5","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":2060,"y":120,"wires":[]},{"id":"e5c713c8408199c6","type":"http response","z":"8a88fee08ad63069","name":"","statusCode":"400","headers":{},"x":2080,"y":340,"wires":[]},{"id":"da0852b8478a5b09","type":"http request","z":"8a88fee08ad63069","name":"http://192.168.86.130:12080/bad","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://192.168.86.130:12080/bad","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":1830,"y":120,"wires":[["2749c38a5d2bc8e3"]]}]

Hi @Steve-Mcl that's great, thanks for the info. However it seems like you would need to add a custom catch node to every tab. Is there a way we could have this catch defined just once and have it forward errors on to the http out node across ALL tabs? Thanks!

Ok, have a solution that seems to be working now that is much cleaner and simpler than the previously discussed solution of adding a catch node to every tab.

Here it is:

RED.hooks.add("onComplete", (e) => {
  if (e.error && e.msg.res) {
    e.msg.res.status(500).json({
      message: e.error,
      source: e.node
    })
  }
})

This is hooking into each node's onComplete handler and capturing any errors on the event and returning those errors using the response object on msg.res.

Simple, clean and effective. This should probably already be the default behavior IMO.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.