Skip to content

Fix error handling for linkcall in function node#5755

Merged
knolleary merged 1 commit into
devfrom
better-error-handling-function-link-call
May 26, 2026
Merged

Fix error handling for linkcall in function node#5755
knolleary merged 1 commit into
devfrom
better-error-handling-function-link-call

Conversation

@Steve-Mcl

Copy link
Copy Markdown
Contributor

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

TLDR

If the call to node.linkcall() errored / timed out, it could not be caught in the function node code & would ALWAYS perform a node.error even when wrapped in a try catch

Proposed changes

Remove try/catch handling so that user-defined function code can catch errors thrown by node.linkcall using a try/catch block, and that such handled errors do not trigger the catch node when they are handled by user code.

Testing enhancements:

  • Added a test to 10-function_spec.js that ensures errors from node.linkcall can be caught within user function code, and confirms that the catch node is not triggered when the error is handled by the user.

Checklist

  • I have read the contribution guidelines
  • For non-bugfix PRs, I have discussed this change on the forum/slack team.
  • I have run npm run test to verify the unit tests pass
  • I have added suitable unit tests to cover the new/changed functionality

@Steve-Mcl

Steve-Mcl commented May 24, 2026

Copy link
Copy Markdown
Contributor Author

Update demo / test flow

image
[{"id":"9fd177895cca5d38","type":"subflow","name":"Subflow 1","info":"","in":[{"x":60,"y":80,"wires":[{"id":"f8cf024cd57106c0"}]}],"out":[{"x":620,"y":80,"wires":[{"id":"f8cf024cd57106c0","port":0}]}]},{"id":"94e5aa990e538ff6","type":"link in","z":"9fd177895cca5d38","name":"subflow-defined-subroutine","links":[],"x":390,"y":200,"wires":[["571cac9f6a7e3774"]],"l":true},{"id":"571cac9f6a7e3774","type":"function","z":"9fd177895cca5d38","name":"call external subroutines double, multiply by 10, format & return msg","func":"\nconst m1 = await node.linkcall('double', msg)\nconst m2 = await node.linkcall('multiply', m1)\nconst m3 = await node.linkcall('format', m2)\n\nreturn m3;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":780,"y":200,"wires":[["10ca19f6214373dc"]]},{"id":"10ca19f6214373dc","type":"link out","z":"9fd177895cca5d38","name":"link out 3","mode":"return","links":[],"x":1065,"y":200,"wires":[]},{"id":"f8cf024cd57106c0","type":"function","z":"9fd177895cca5d38","name":"call subflow-defined-subroutine (defined in this subflow)","func":"\nreturn await node.linkcall('subflow-defined-subroutine', msg)\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":330,"y":80,"wires":[[]]},{"id":"aa5f30fe9e858442","type":"comment","z":"9fd177895cca5d38","name":"↑ this, func calls own internal subroutine ↓","info":"","x":340,"y":140,"wires":[]},{"id":"f3835fd04c40d1a0","type":"comment","z":"9fd177895cca5d38","name":"↑ this calls outside to non-subflow subroutines!","info":"","x":850,"y":260,"wires":[]},{"id":"cc414bda5adfb096","type":"tab","label":"Reusable Functions","disabled":false,"info":"","env":[]},{"id":"ce2649a9d1250e95","type":"group","z":"cc414bda5adfb096","name":"Testing behaviour when the link-return node is missing","style":{"label":true},"nodes":["4f7d2d3b40d3f7f4","13523ad88a9482ac","daf8355bc96f530b","4825f715341f90c1","23943353e7d2b67e","20d44d546533df01","0c481993c23f26f9","ce1a9334a496adba","87717a574e91da47","341f208105d747ce","bd5c576b0a04a91d","6b8d5a61cfda47d1","3cc0d60f8b8b28fa","251fc29ee6edbc71","d24afdb7a5c2ac39"],"x":1134,"y":19,"w":812,"h":342},{"id":"25234a6b6d427d32","type":"group","z":"cc414bda5adfb096","name":"Testing behaviour when a non-existing target is specified","style":{"label":true},"nodes":["4609cc1779093e54","5f09f8a4e18f14fa","d946d88abde9f20e","baf1f778cc98a541","d2ac5b14ca6f8f05","d34dff81efc99a69","5ad86cadaca0d9d3","afc198676de33515","bfd4a958c2050e88","4b7060e9fc303dbf","16ad85350f0629d6"],"x":1134,"y":379,"w":832,"h":222},{"id":"3d00df31c3554e9d","type":"group","z":"cc414bda5adfb096","name":"Demonstrate sequential calls: \\n - In the 1st demo the function node calls all 3 link-in subroutines. \\n - In the 2nd demo, a subflow calls an internal subroutine that then call new linkcall API to call out to these 3 subroutines defined below. \\n - In the 3rd demo, link-call nodes (with static targets) are used to demonstrate original functionality","style":{"label":true,"color":"#000000"},"nodes":["c15bf07f3a3fdef3","5b7b23a6c22b8dae","90bb04d8cb79cad9","b4e4175d5aa8a2c5","cefc0df2258b5d0f","802bae134d859720","47eb8d1427c22774","e7b9ce8ba3662422","12374a8730292001","79e9adb69065d4dc","7034fb06a1b271c4","2d12375f9c6c9a09","b5c90154c3cbdbfb","c0d19461bce5682b","793eed7e19759df2","5422c0afdf996c1e","dbc46892c8d14c37","e10575d73f2e5352","cf8438e7137bc0f0","e567e781ba9fc82e","f126947ce51e823d","327007943fb1d823","2edba7115350acfe","aa8db6b2156a343a","1af1b719b9ee6229","9a089f249abdefec","e0d58b527a6c943b","3acd2b5e8d6be131","46bfd34694e2b91e"],"x":154,"y":31,"w":852,"h":470},{"id":"8bd773cb94a65c0e","type":"group","z":"cc414bda5adfb096","name":"A function library example","style":{"label":true},"nodes":["e56a2c5588f418f2","22d101127acdf47c","4c12c4b1ffeb8df8","6c4d41ed5cbc6252","8d818b321f92e8b8","15a6e33d9b331198","c9b143eee9ebb9ab","9e6d871d6b24d40e","a8e6f2a4f0d68927","781df7af7f5f7297","6522251212b3fb45"],"x":154,"y":1059,"w":1092,"h":266},{"id":"d44d3240a6cb97f2","type":"group","z":"cc414bda5adfb096","name":"Demonstrating Nested calls: Calls nested-1 -> nested-2 -> nested-3 (nested-3 is on another tab) \\n Each nested subroutine adds a message to payload array. \\n If the nested subroutine calls another subroutine, it adds another message after its call resolves.","style":{"label":true},"nodes":["3ab5b83df9ae5de6","639b814545d80ebf","3859f9364d7e20e4","481b18a69648c7b7","5dca5145da4121aa","de4bdc4d6dca2e9c","468c21fadafb2b1f","d32794dfc67cfb39","2cd720b44e2a35fd","36d47b1e1e84d941","825beca55485daab","dc5e3e573b5c784f","483a2f4412eefe45","e609b189f96e6b5f","69d651c9992480b3","9a640eacc5eac686","dc69d07ec94599dc","d7efa5c524a4cd50","1a1579aab5b655a0","81ac614071fcf9ed"],"x":154,"y":527,"w":712,"h":514},{"id":"63f7d17579c25093","type":"group","z":"cc414bda5adfb096","name":"Testing behaviour of bad parameters","style":{"label":true},"nodes":["ae43905c5e1135ce","5bedcff6dbe93969","e1420b90129fb6d7","d15a33377ef25afb","f23786150d441a6f","ddcfd3f4586a9683","73d8ec5425f002a6","648ca39cf1b8c0be"],"x":1134,"y":619,"w":472,"h":202},{"id":"1835110d9ba6d157","type":"group","z":"cc414bda5adfb096","name":"Testing behaviour of clone option","style":{"label":true},"nodes":["91a3ed84a5288d77","ed8d802c06a44a3c","881d277cbdd78570","eb426ab6c8c6d785","8d4b2ce851ea9aac","b06c14ea9ded06f8","2c56619b9c0269a6"],"x":1134,"y":839,"w":532,"h":182},{"id":"2dd6e927bebf04b1","type":"group","z":"cc414bda5adfb096","name":"no try..catch, no catch node","style":{"label":true},"nodes":["41f2fec6936b075a","e3f07ee3971cc50e","9a43d9b130e9b490"],"x":2034,"y":379,"w":312,"h":162},{"id":"943ae6f5c99c0ed0","type":"group","z":"cc414bda5adfb096","name":"no try..catch, no catch node","style":{"label":true},"nodes":["8af82e398ab7bf25","70f1d578201a15c9"],"x":2034,"y":239,"w":312,"h":122},{"id":"4f7d2d3b40d3f7f4","type":"function","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"no-return-target, timeout 1 sec","func":"\nconst m1 = await node.linkcall('no-return-target', msg, { timeout: 1000 })\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1550,"y":60,"wires":[[]]},{"id":"13523ad88a9482ac","type":"inject","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":60,"wires":[["4f7d2d3b40d3f7f4"]]},{"id":"87717a574e91da47","type":"link in","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"no-return-target","links":[],"x":1300,"y":320,"wires":[["341f208105d747ce"]],"l":true},{"id":"341f208105d747ce","type":"debug","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"no-return-target called","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1520,"y":320,"wires":[]},{"id":"3ab5b83df9ae5de6","type":"link in","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"nested-1","links":[],"x":280,"y":800,"wires":[["639b814545d80ebf","481b18a69648c7b7"]],"l":true},{"id":"639b814545d80ebf","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"input","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":310,"y":840,"wires":[]},{"id":"3859f9364d7e20e4","type":"link out","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"","mode":"return","links":[],"x":645,"y":800,"wires":[]},{"id":"481b18a69648c7b7","type":"function","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"call nested-2","func":"\nmsg.payload.push(\"Hello from nested-1, I will call nested-2\")\n\nconst m = await node.linkcall('nested-2', msg, { timeout: 1000 })\n\nm.payload.push('Back in nested 1 (after call to nested-2)')\n\nreturn m;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":800,"wires":[["3859f9364d7e20e4","e609b189f96e6b5f"]]},{"id":"5dca5145da4121aa","type":"link in","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"nested-2","links":[],"x":280,"y":900,"wires":[["de4bdc4d6dca2e9c","9a640eacc5eac686"]],"l":true},{"id":"de4bdc4d6dca2e9c","type":"function","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"call nested-3","func":"\nmsg.payload.push(\"Hello from nested-2, I will call nested-3\")\n\nconst m = await node.linkcall('nested-3', msg, { timeout: 1000 })\n\nm.payload.push('Back in nested 2 (after call to nested-3)')\n\nreturn m;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":470,"y":900,"wires":[["468c21fadafb2b1f","69d651c9992480b3"]]},{"id":"468c21fadafb2b1f","type":"link out","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"","mode":"return","links":[],"x":635,"y":900,"wires":[]},{"id":"d32794dfc67cfb39","type":"function","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"call nested-1","func":"\n\nconst returnMsg = await node.linkcall('nested-1', msg, { timeout: 1000 })\nreturn returnMsg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":590,"y":600,"wires":[["dc69d07ec94599dc"]]},{"id":"2cd720b44e2a35fd","type":"inject","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"[\"1st payload for function\"]","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"1st payload for function\"]","payloadType":"json","x":310,"y":600,"wires":[["d32794dfc67cfb39"]]},{"id":"36d47b1e1e84d941","type":"link call","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"","links":["3ab5b83df9ae5de6"],"linkType":"static","timeout":"30","x":600,"y":660,"wires":[["d7efa5c524a4cd50"]]},{"id":"825beca55485daab","type":"inject","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"[\"1st payload for static link-call\"]","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"1st payload for static link-call\"]","payloadType":"json","x":330,"y":660,"wires":[["36d47b1e1e84d941"]]},{"id":"dc5e3e573b5c784f","type":"link call","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"","links":["3ab5b83df9ae5de6"],"linkType":"dynamic","timeout":"2","x":600,"y":720,"wires":[["1a1579aab5b655a0"]]},{"id":"483a2f4412eefe45","type":"inject","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"[\"1st payload for dynamic link-call\"]","props":[{"p":"payload"},{"p":"target","v":"nested-1","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"1st payload for dynamic link-call\"]","payloadType":"json","x":340,"y":720,"wires":[["dc5e3e573b5c784f"]]},{"id":"e609b189f96e6b5f","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"output","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":610,"y":840,"wires":[]},{"id":"69d651c9992480b3","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"output","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":610,"y":940,"wires":[]},{"id":"9a640eacc5eac686","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"input","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":310,"y":940,"wires":[]},{"id":"dc69d07ec94599dc","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":770,"y":600,"wires":[]},{"id":"d7efa5c524a4cd50","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":770,"y":660,"wires":[]},{"id":"1a1579aab5b655a0","type":"debug","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"output","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"\"payload len: \" & $count(payload)","statusType":"jsonata","x":770,"y":720,"wires":[]},{"id":"81ac614071fcf9ed","type":"comment","z":"cc414bda5adfb096","g":"d44d3240a6cb97f2","name":"↑ \"nested-3\" is on \"more subroutine\" tab","info":"","x":550,"y":1000,"wires":[]},{"id":"daf8355bc96f530b","type":"link call","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"fixed target no-return-target","links":["87717a574e91da47"],"linkType":"static","timeout":"1","x":1540,"y":140,"wires":[[]]},{"id":"4825f715341f90c1","type":"inject","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":140,"wires":[["daf8355bc96f530b"]]},{"id":"23943353e7d2b67e","type":"catch","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","scope":"group","uncaught":false,"x":1230,"y":220,"wires":[["20d44d546533df01"]]},{"id":"20d44d546533df01","type":"debug","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":1470,"y":220,"wires":[]},{"id":"0c481993c23f26f9","type":"link call","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"dynamic link call","links":["87717a574e91da47"],"linkType":"dynamic","timeout":"1","x":1500,"y":180,"wires":[[]]},{"id":"ce1a9334a496adba","type":"inject","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"target=no-return-target","props":[{"p":"target","v":"no-return-target","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1280,"y":180,"wires":[["0c481993c23f26f9"]]},{"id":"4609cc1779093e54","type":"function","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"call non-existant target","func":"\nconst m1 = await node.linkcall(msg.target, msg, { timeout: 2000 })\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1520,"y":420,"wires":[["5f09f8a4e18f14fa"]]},{"id":"5f09f8a4e18f14fa","type":"debug","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"debug 14","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1740,"y":420,"wires":[]},{"id":"d946d88abde9f20e","type":"inject","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"target=bad-target","props":[{"p":"target","v":"bad-target","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1260,"y":460,"wires":[["baf1f778cc98a541"]]},{"id":"baf1f778cc98a541","type":"link call","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"dynamic link call","links":[],"linkType":"dynamic","timeout":"30","x":1500,"y":460,"wires":[["d2ac5b14ca6f8f05"]]},{"id":"d2ac5b14ca6f8f05","type":"debug","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"debug 19","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1740,"y":460,"wires":[]},{"id":"d34dff81efc99a69","type":"inject","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"target=bad-target","props":[{"p":"target","v":"bad-target","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1260,"y":420,"wires":[["4609cc1779093e54"]]},{"id":"5ad86cadaca0d9d3","type":"catch","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"","scope":"group","uncaught":false,"x":1250,"y":560,"wires":[["afc198676de33515"]]},{"id":"afc198676de33515","type":"debug","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"","active":true,"tosidebar":true,"console":true,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":1470,"y":560,"wires":[]},{"id":"c15bf07f3a3fdef3","type":"inject","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"(4 doubled) *5 = 40","props":[{"p":"payload"},{"p":"multiplier","v":"5","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"4","payloadType":"num","x":290,"y":180,"wires":[["f126947ce51e823d"]]},{"id":"5b7b23a6c22b8dae","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 13","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":900,"y":180,"wires":[]},{"id":"90bb04d8cb79cad9","type":"inject","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"(5 doubled) *10 = 100","props":[{"p":"payload"},{"p":"multiplier","v":"15","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"5","payloadType":"num","x":300,"y":240,"wires":[["b4e4175d5aa8a2c5"]]},{"id":"b4e4175d5aa8a2c5","type":"link call","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","links":["e7b9ce8ba3662422"],"linkType":"static","timeout":"1","x":490,"y":240,"wires":[["cefc0df2258b5d0f"]]},{"id":"cefc0df2258b5d0f","type":"link call","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","links":["b5c90154c3cbdbfb"],"linkType":"static","timeout":"1","x":620,"y":240,"wires":[["802bae134d859720"]]},{"id":"802bae134d859720","type":"link call","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","links":["dbc46892c8d14c37"],"linkType":"static","timeout":"1","x":750,"y":240,"wires":[["47eb8d1427c22774"]]},{"id":"47eb8d1427c22774","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 20","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":900,"y":240,"wires":[]},{"id":"e56a2c5588f418f2","type":"function","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"bit-functions","func":"// A bit-functions utility library\n// * bitShift\n// * bitSet\n// * bitClear\n// * bitState\n\n// Define the lib functions\nfunction bitShift (num, direction, positions) {\n    if (direction === 'left') {\n        return num << positions\n    } else if (direction === 'right') {\n        return num >> positions\n    } else {\n        throw new Error('Invalid direction. Use \"left\" or \"right\".')\n    }\n}\nfunction bitSet(num, position) {\n    return num | (1 << position)\n}\nfunction bitClear (num, position) {\n    return num & ~(1 << position)\n}\nfunction bitState(num, position) {\n    return (num & (1 << position)) !== 0\n}\n\n// Create a function look up\nconst libraryLookup = {\n    bitShift,\n    bitSet,\n    bitClear,\n    bitState\n}\n\n// Get the requested function from library and call it\nif (!Array.isArray(msg.payload) || !msg.payload.length) {\n    throw new Error('Expected payload to be an array with the function name (and optionally, any aruments)')\n}\nconst [functionName, ...args] = msg.payload\nconst utilityFunction = libraryLookup[functionName]\nif (!utilityFunction) {\n    throw new Error(`Function '${functionName}' not found!`)\n}\nconst result = utilityFunction(...args)\nmsg.payload = result\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":510,"y":1240,"wires":[["4c12c4b1ffeb8df8"]]},{"id":"22d101127acdf47c","type":"link in","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"bit-functions","links":[],"x":330,"y":1240,"wires":[["6c4d41ed5cbc6252","e56a2c5588f418f2"]],"l":true},{"id":"4c12c4b1ffeb8df8","type":"link out","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"link out 2","mode":"return","links":[],"x":635,"y":1240,"wires":[]},{"id":"6c4d41ed5cbc6252","type":"debug","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"input","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":490,"y":1280,"wires":[]},{"id":"8d818b321f92e8b8","type":"inject","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":250,"y":1100,"wires":[["781df7af7f5f7297"]]},{"id":"15a6e33d9b331198","type":"debug","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"bitSet result","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":810,"y":1100,"wires":[]},{"id":"c9b143eee9ebb9ab","type":"function","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"call bit-functions->bitShift payload left by 2","func":"msg.payload = ['bitShift', msg.payload, 'left', 2]\nreturn await node.linkcall('bit-functions', msg)\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":1160,"wires":[["a8e6f2a4f0d68927"]]},{"id":"9e6d871d6b24d40e","type":"inject","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"1","payloadType":"num","x":250,"y":1160,"wires":[["c9b143eee9ebb9ab"]]},{"id":"a8e6f2a4f0d68927","type":"debug","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"bitShift result","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":810,"y":1160,"wires":[]},{"id":"dbc46892c8d14c37","type":"link in","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"format","links":[],"x":270,"y":420,"wires":[["e10575d73f2e5352","3acd2b5e8d6be131"]],"l":true},{"id":"e10575d73f2e5352","type":"change","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"format result","rules":[{"t":"set","p":"payload","pt":"msg","to":"\"The result is: \" & payload","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":420,"wires":[["cf8438e7137bc0f0","e567e781ba9fc82e"]]},{"id":"cf8438e7137bc0f0","type":"link out","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","mode":"return","links":[],"x":545,"y":420,"wires":[]},{"id":"12374a8730292001","type":"change","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"double","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload * 2","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":320,"wires":[["7034fb06a1b271c4","2d12375f9c6c9a09"]]},{"id":"e7b9ce8ba3662422","type":"link in","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"double","links":[],"x":270,"y":320,"wires":[["12374a8730292001","79e9adb69065d4dc"]],"l":true},{"id":"7034fb06a1b271c4","type":"link out","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","mode":"return","links":[],"x":545,"y":320,"wires":[]},{"id":"c0d19461bce5682b","type":"change","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"* multiplier","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload * multiplier","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":320,"wires":[["793eed7e19759df2","5422c0afdf996c1e"]]},{"id":"b5c90154c3cbdbfb","type":"link in","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"multiply","links":[],"x":650,"y":320,"wires":[["c0d19461bce5682b","1af1b719b9ee6229"]],"l":true},{"id":"793eed7e19759df2","type":"link out","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"","mode":"return","links":[],"x":935,"y":320,"wires":[]},{"id":"e567e781ba9fc82e","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 10","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":545,"y":460,"wires":[],"l":false},{"id":"79e9adb69065d4dc","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"input","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":270,"y":360,"wires":[]},{"id":"2d12375f9c6c9a09","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 12","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":545,"y":360,"wires":[],"l":false},{"id":"5422c0afdf996c1e","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 18","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":935,"y":360,"wires":[],"l":false},{"id":"f126947ce51e823d","type":"subflow:9fd177895cca5d38","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"call subflow has internal SBN that calls these 3","x":580,"y":180,"wires":[["5b7b23a6c22b8dae"]]},{"id":"5bedcff6dbe93969","type":"inject","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":700,"wires":[["e1420b90129fb6d7"]]},{"id":"e1420b90129fb6d7","type":"function","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"missing msg property","func":"msg.payload = ['bitSet', msg.payload, 4]\nreturn await node.linkcall('bit-functions')\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1420,"y":700,"wires":[[]]},{"id":"d15a33377ef25afb","type":"function","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"incorrect target parameter type","func":"msg.payload = ['bitSet', msg.payload, 4]\nreturn await node.linkcall(msg, msg)\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1450,"y":740,"wires":[[]]},{"id":"f23786150d441a6f","type":"inject","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":740,"wires":[["d15a33377ef25afb"]]},{"id":"781df7af7f5f7297","type":"function","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"call bit-functions->bitSet set bit 3 in payload","func":"msg.payload = ['bitSet', msg.payload, 3]\nreturn await node.linkcall('bit-functions', msg)\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":1100,"wires":[["15a6e33d9b331198"]]},{"id":"327007943fb1d823","type":"inject","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"(2 doubled) * 5 = 20","props":[{"p":"payload"},{"p":"multiplier","v":"5","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"2","payloadType":"num","x":290,"y":120,"wires":[["aa8db6b2156a343a"]]},{"id":"2edba7115350acfe","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 26","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":900,"y":120,"wires":[]},{"id":"aa8db6b2156a343a","type":"function","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"call double, multiply, format then return","func":"\nconst m1 = await node.linkcall('double', msg)\nconst m2 = await node.linkcall('multiply', m1)\nconst m3 = await node.linkcall('format', m2)\n\nreturn m3;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":120,"wires":[["2edba7115350acfe"]]},{"id":"ae43905c5e1135ce","type":"inject","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":660,"wires":[["ddcfd3f4586a9683"]]},{"id":"ddcfd3f4586a9683","type":"function","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"missing target property","func":"return await node.linkcall()\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1420,"y":660,"wires":[[]]},{"id":"73d8ec5425f002a6","type":"inject","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":780,"wires":[["648ca39cf1b8c0be"]]},{"id":"648ca39cf1b8c0be","type":"function","z":"cc414bda5adfb096","g":"63f7d17579c25093","name":"incorrect msg parameter type","func":"msg.payload = ['bitSet', msg.payload, 4]\nreturn await node.linkcall('bit-functions', 'hello')\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1440,"y":780,"wires":[[]]},{"id":"bd5c576b0a04a91d","type":"function","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"no-return-target, default timeout (5 sec)","func":"\nconst m1 = await node.linkcall('no-return-target', msg)\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1580,"y":100,"wires":[[]]},{"id":"6b8d5a61cfda47d1","type":"inject","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":100,"wires":[["bd5c576b0a04a91d"]]},{"id":"91a3ed84a5288d77","type":"function","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"clones message (default behaviour)","func":"\nmsg.payload = {\n    bool: false,\n    string: \"original\",\n    number: 0,\n    array: [],\n    object: {}\n}\nmsg.more = \"original\"\nmsg.evenmore = {\n    string: 'original'\n}\n\nconst msgBefore = JSON.parse(JSON.stringify(msg))\nconst result = await node.linkcall('modify-msg', msg /*, { clone: true }*/)\nconst msgAfter = JSON.parse(JSON.stringify(msg))\n\n\nassert.deepEqual(msgBefore, msgAfter)\n\nnode.warn(\"Assertions passed ✅\");\n\nreturn result;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"assert","module":"assert"}],"x":1480,"y":880,"wires":[[]]},{"id":"ed8d802c06a44a3c","type":"inject","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":880,"wires":[["91a3ed84a5288d77"]]},{"id":"881d277cbdd78570","type":"link in","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"modify-msg","links":[],"x":1270,"y":980,"wires":[["8d4b2ce851ea9aac"]],"l":true},{"id":"eb426ab6c8c6d785","type":"link out","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"link out 4","mode":"return","links":[],"x":1575,"y":980,"wires":[]},{"id":"8d4b2ce851ea9aac","type":"function","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"modify-msg","func":"\nmsg.payload.bool = true,\nmsg.payload.string = \"modified\",\nmsg.payload.number = 1,\nmsg.payload.array.push(1,2,3,4,5),\n\nmsg.payload.object.newProp = 42\n\nmsg.more = \"modified\"\n\nmsg.evenmore = {\n    string: \"modified\"\n}\n\nmsg.newProp = 42\n\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"assert","module":"assert"}],"x":1450,"y":980,"wires":[["eb426ab6c8c6d785"]]},{"id":"b06c14ea9ded06f8","type":"function","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"mutates message (clone:false option )","func":"\nmsg.payload = {\n    bool: false,\n    string: \"hello\",\n    number: 0,\n    array: [],\n    object: {}\n}\nmsg.more = \"more\"\nmsg.evenmore = {\n    string: 'original'\n}\n\n\nconst msgBefore = JSON.parse(JSON.stringify(msg))\nconst result = await node.linkcall('modify-msg', msg, { clone: false })\nconst msgAfter = JSON.parse(JSON.stringify(msg))\n\n\nassert.equal(msg.payload.bool, true) // modified by ref\nassert.equal(msg.payload.string, 'modified') // modified by ref\nassert.equal(msg.payload.number, 1) // modified by ref\nassert.equal(msg.payload.object.newProp, 42) // added by ref\n\nassert.equal(msg.more, \"modified\") // modified by ref\nassert.equal(msg.newProp, 42) // added by ref\n\nassert.notDeepEqual(msgBefore, msgAfter)\n\nnode.warn(\"Assertions passed ✅\");\n\nreturn result;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"assert","module":"assert"}],"x":1490,"y":920,"wires":[[]]},{"id":"2c56619b9c0269a6","type":"inject","z":"cc414bda5adfb096","g":"1835110d9ba6d157","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":920,"wires":[["b06c14ea9ded06f8"]]},{"id":"1af1b719b9ee6229","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"debug 27","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"multiplier","targetType":"msg","statusVal":"payload","statusType":"auto","x":755,"y":360,"wires":[],"l":false},{"id":"9a089f249abdefec","type":"comment","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"✖️","info":"","x":650,"y":374,"wires":[]},{"id":"e0d58b527a6c943b","type":"comment","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"🟰","info":"","x":850,"y":374,"wires":[]},{"id":"3acd2b5e8d6be131","type":"debug","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"input","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":270,"y":460,"wires":[]},{"id":"46bfd34694e2b91e","type":"comment","z":"cc414bda5adfb096","g":"3d00df31c3554e9d","name":"Doubled 🟰","info":"","x":430,"y":375,"wires":[]},{"id":"6522251212b3fb45","type":"comment","z":"cc414bda5adfb096","g":"8bd773cb94a65c0e","name":"\"bit-functions\" contains handy utility functions (bitShift, bitSet, bitClear, bitState) \\n These can be called by passing an array in payload [name, ...params] \\n this pattern is user defined - included here to demonstration possibilities","info":"","x":950,"y":1260,"wires":[]},{"id":"bfd4a958c2050e88","type":"function","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"TRY..CATCH: call non-existant target","func":"let m1\ntry {\n    m1 = await node.linkcall(msg.target, msg, { timeout: 2000 })\n} catch (error) {\n    const message = `${Date.now()} err: ${error.message}`\n    node.status({fill:\"red\",shape:\"ring\",text:message});\n    msg.error = error\n    return msg\n};\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1570,"y":500,"wires":[["16ad85350f0629d6"]]},{"id":"4b7060e9fc303dbf","type":"inject","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"target=bad-target","props":[{"p":"target","v":"bad-target","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1260,"y":500,"wires":[["bfd4a958c2050e88"]]},{"id":"16ad85350f0629d6","type":"debug","z":"cc414bda5adfb096","g":"25234a6b6d427d32","name":"debug 9","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1860,"y":500,"wires":[]},{"id":"3cc0d60f8b8b28fa","type":"function","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"TRY..CATCH: no-return-target, timeout 1 sec","func":"let m1\ntry {\n    m1 = await node.linkcall('no-return-target', msg, { timeout: 1000 })\n} catch (error) {\n    const message = `${Date.now()} err: ${error.message}`\n    node.status({fill:\"red\",shape:\"ring\",text:message});\n    msg.error = error\n    return msg\n};\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1590,"y":260,"wires":[["d24afdb7a5c2ac39"]]},{"id":"251fc29ee6edbc71","type":"inject","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1230,"y":260,"wires":[["3cc0d60f8b8b28fa"]]},{"id":"d24afdb7a5c2ac39","type":"debug","z":"cc414bda5adfb096","g":"ce2649a9d1250e95","name":"debug 11","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1840,"y":260,"wires":[]},{"id":"41f2fec6936b075a","type":"inject","z":"cc414bda5adfb096","g":"2dd6e927bebf04b1","name":"target=bad-target","props":[{"p":"target","v":"bad-target","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":2160,"y":420,"wires":[["e3f07ee3971cc50e"]]},{"id":"e3f07ee3971cc50e","type":"function","z":"cc414bda5adfb096","g":"2dd6e927bebf04b1","name":"call non-existant target","func":"\nconst m1 = await node.linkcall(msg.target, msg, { timeout: 2000 })\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2160,"y":460,"wires":[["9a43d9b130e9b490"]]},{"id":"9a43d9b130e9b490","type":"debug","z":"cc414bda5adfb096","g":"2dd6e927bebf04b1","name":"debug 15","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":2240,"y":500,"wires":[]},{"id":"8af82e398ab7bf25","type":"function","z":"cc414bda5adfb096","g":"943ae6f5c99c0ed0","name":"no-return-target, timeout 1 sec","func":"\nconst m1 = await node.linkcall('no-return-target', msg, { timeout: 1000 })\nreturn m1;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":2190,"y":320,"wires":[[]]},{"id":"70f1d578201a15c9","type":"inject","z":"cc414bda5adfb096","g":"943ae6f5c99c0ed0","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":2130,"y":280,"wires":[["8af82e398ab7bf25"]]}]

@Steve-Mcl Steve-Mcl requested a review from knolleary May 24, 2026 17:52
@knolleary knolleary merged commit b853fd1 into dev May 26, 2026
3 checks passed
@knolleary knolleary deleted the better-error-handling-function-link-call branch May 26, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants