Nodered keeps restarting with processTicksAndRejections error

Hello everyone,

I'm actually experiencing some troubles with Nodered. Following my raspberry pi crash I needed to redo the complete setup, so I install a fresh Nodered v3 and used a flow backup that I made not long ago.

The trouble is that now a specific node is making a mess and nodered is restarting after the flow start running and the error occuring. I need to modify the flows.json to delete this node so it stops restarting.

The node I believe to get me trouble is the surepetcare one.

Welcome to Node-RED

26 Mar 11:58:30 - [info] Node-RED version: v3.0.2
26 Mar 11:58:30 - [info] Node.js version: v16.19.1
26 Mar 11:58:30 - [info] Linux 6.1.19-v7+ arm LE
26 Mar 11:58:33 - [info] Loading palette nodes
26 Mar 11:58:39 - [info] Worldmap version 2.34.0
26 Mar 11:58:40 - [info] Dashboard version 3.4.0 started at /ui
26 Mar 11:58:40 - [info] Settings file : /home/klose54/.node-red/settings.js
26 Mar 11:58:40 - [info] Context store : 'default' [module=memory]
26 Mar 11:58:40 - [info] User directory : /home/klose54/.node-red
26 Mar 11:58:40 - [warn] Projects disabled : editorTheme.projects.enabled=false
26 Mar 11:58:40 - [info] Flows file : /home/klose54/.node-red/flows.json
26 Mar 11:58:40 - [info] Server now running at http://127.0.0.1:1880/
26 Mar 11:58:40 - [warn]

Your flow credentials file is encrypted using a system-generated key.
If the system-generated key is lost for any reason, your credentials
file will not be recoverable, you will have to delete it and re-enter
your credentials.
You should set your own key using the 'credentialSecret' option in
your settings file. Node-RED will then re-encrypt your credentials
file using your chosen key the next time you deploy a change.

26 Mar 11:58:44 - [info] Starting flows
26 Mar 11:58:44 - [info] [surepet-credentials:b5cc3c783ef12564] We have a username and password, attempting to authenticiate.

....................

26 Mar 11:59:16 - [red] Uncaught Exception:
26 Mar 11:59:16 - [error] Error: Get state failed: unauthenticated
at SurePetCareClient.getState (/home/klose54/.node-red/node_modules/sure-pet-care-client/dist/client.js:39:31)
at processTicksAndRejections (node:internal/process/task_queues:96:5)

The thing is it seems to be and authentification problem, but when the node is manually called it works. Everytime I put an inject node before so I automatically call the node, it keeps getting me this error and nodered is restarting ...

Does anyone can help me ?

How are you using sure-pet-care-client - npm ? in a function node? If so, you need to handle the error.

To start node-red to get access & fix up your flows, you can start in safe mode.

e.g. node-red --safe

Hello and thank you for your reply.

I'm using it as the function node with only credentials in it and getting payload as I always do. I've never made anything specific to handle errors. How should I make this ?

image

Thank you for the safe command, this will be useful.

Actually that looks like a contrib node.

In short, a contrib node should NOT crash node-red, regardless of bad credentials or misconfiguration.

You should raise an issue on the repository: Issues · jpwsutton/node-red-contrib-surepet · GitHub

I believe the issue is they are not handling the promise .then & .catch here: node-red-contrib-surepet/surepet.js at 548e3d50560bb9af4b4a38195c72e43495da02d0 · jpwsutton/node-red-contrib-surepet · GitHub

Thank you for your help, I raised an issue on the github.

If it is only on startup that you are getting the problem, then a workaround may be to configure the inject node to trigger a little time after startup. Set it to 60 seconds initially and see what happens.

It seems to be working for now, I changed the inject nodes as you said, reboot nodered for a few times and its working fine, let's see for a longer period. Thanks for the idea.

Possibly, the bug is being triggered if node red starts up before the network is connected, which is likely if you are using wifi. In the node red systemd startup script, there is a line you can uncomment which will make it wait for a time sync on boot (which means the network must be running) before starting node-red, so you could try that instead. It does have the disadvantage that if you it cannot connect then node red will never start, which can be confusing.

So since that i delayed for a few seconds the first call of this node does improve and never does the same issue again.

But now it happens a lot of time that nodered restart on this :

[red] Uncaught Exception:
[error] Error: Get state failed: received 504 Gateway Time-out
    at SurePetCareClient.getState (/home/klose54/.node-red/node_modules/sure-pet-care-client/dist/client.js:41:31)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Since yesterday I have this like 5 times per hour, which can an issue. Maybe the contrib node is not working well with a new version of something ? It seems to be not updated since 3 years.

I believe that the creator of this contrib is no longer active on this one and that the github message may be a no go ...

The problem is that everytime the server seems to be down for a short time, my nodered keeps rebooting.

Is there anybody that knows what to change in this part of the code for the node to make an error message instead of crashing on a 504 gateway time-out failure ?


function SurePetCredentials(n){
        RED.nodes.createNode(this, n);
        this.username = n.username || this.credentials.username;
        this.password = n.password || this.credentials.password;
        this.client = new SurePet.SurePetCareClient();
        if (this.username && this.password){
            this.log("We have a username and password, attempting to authenticiate.")
            try{
                this.client.authenticate(this.username, this.password);
                this.status({fill:"green",shape:"dot",text:"Connected."});
            } catch (err){
                this.status({fill:"red",shape:"ring",text:err});
            }
           
        }
    }

Since that function is async (see docs here)

Authenticate

Authenticates the user using email and password.

client.authenticate(email: string, password: string): Promise<void>;

Note the returns promise :point_up: that indicates it is async.

So, use the await keyword

            try{
                await this.client.authenticate(this.username, this.password);
                this.status({fill:"green",shape:"dot",text:"Connected."});
            } catch (err){
                this.status({fill:"red",shape:"ring",text:err});
            }

This will cause the try ~ catch to actually catch the error.

Note also, getPets and getState are also async. They have .then but no .catch so I would also change...

        node.on('input', function(msg){

            if(config.mode === 'list_pets'){
            this.surepet.client.getPets().then((pets) =>{
                msg.payload = {
                    pets: pets
                }
                node.send(msg)
            })
        } else if(config.mode === "get_status"){
            this.surepet.client.getState().then((state) => {
                msg.payload = state;
                node.send(msg);
            })

        }

to

node.on('input', function (msg) {
    if (config.mode === 'list_pets') {
        this.surepet.client.getPets().then((pets) => {
            msg.payload = {
                pets: pets
            }
            node.send(msg)
        }).catch(e => {
            node.error(e, msg)
        })
    } else if (config.mode === "get_status") {
        this.surepet.client.getState().then((state) => {
            msg.payload = state;
            node.send(msg);
        }).catch(e => {
            node.error(e, msg)
        })
    }
})

Thank you for your help @Steve-Mcl

I've made the edit in the surepet.js, It has been well deployed and running for a few minutes now, I will give you some news after some runtime.

EDIT : In fact, I thought I made the changes but I have two surepet.js files in my directory, one in /node_modules/node-red-contrib-surepet/surepet.js where I made the edit, and an other in /.node-red/node_modules/node-red-contrib-surepet/surepet.js where when I try to do the changes you gave me, nodered cant restart and it gaves me this :

 [info] Waiting for missing types to be registered:
 [info]  - surepet-credentials
 [info]  - surepet

It is likely you have a syntax error.

Open the file in a proper editor (eg VSCode) & it should reveal any issues.

So the entire code with your modifications is

module.exports = function(RED){
    "use strict"
    var SurePet = require('sure-pet-care-client');

    function SurePetNode(config){
        RED.nodes.createNode(this, config);
        this.surepet = RED.nodes.getNode(config.surepet);
        var node = this;
        //console.log(config);
        node.on('input', function(msg){

            if(config.mode === 'list_pets'){
            this.surepet.client.getPets().then((pets) =>{
                msg.payload = {
                    pets: pets
                }
                node.send(msg)
            }).catch(e => {
                node.error(e, msg)
            })
        } else if(config.mode === "get_status"){
            this.surepet.client.getState().then((state) => {
                msg.payload = state;
                node.send(msg);
            }).catch(e => {
                node.error(e, msg)
            })

        }
            
            /*
            msg.payload = {
                surepet: this.surepet,
                pets: 
            }
            node.send(msg);*/
        });
    }
    RED.nodes.registerType("surepet", SurePetNode);



    function SurePetCredentials(n){
        RED.nodes.createNode(this, n);
        this.username = n.username || this.credentials.username;
        this.password = n.password || this.credentials.password;
        this.client = new SurePet.SurePetCareClient();
        if (this.username && this.password){
            this.log("We have a username and password, attempting to authenticiate.")
            try{
                await this.client.authenticate(this.username, this.password);
                this.status({fill:"green",shape:"dot",text:"Connected."});
            } catch (err){
                this.status({fill:"red",shape:"ring",text:err});
            }
           
        }
    }
    RED.nodes.registerType("surepet-credentials",SurePetCredentials,{
        credentials: {
            username: {type:"text"},
            password: {type:"password"}
        }
    });
}

After testing on VSCode, it stated that

'await' expressions are only allowed within async functions and at the top levels of modules.

So the soft added a async at

async function SurePetCredentials(n){

With that, VSCode stated no problem detected. But I still have a trouble on deploy of nodered that indicate the same error waiting for missing types, with this line showing during the deploy

[node-red-contrib-surepet/surepet] TypeError: Cannot convert undefined or null to object (line:55)

Any idea ?

Ah ok, try changing this function...

function SurePetCredentials(n){
        RED.nodes.createNode(this, n);
        this.username = n.username || this.credentials.username;
        this.password = n.password || this.credentials.password;
        this.client = new SurePet.SurePetCareClient();
        const node = this
        if (this.username && this.password){
            this.log("We have a username and password, attempting to authenticate.")
            
            this.client.authenticate(this.username, this.password)
           .then(() => node.status({fill:"green",shape:"dot",text:"Connected.")
           .catch(err => node.status({fill:"red",shape:"ring",text:err}))
        }
    }

written on mobile, untested

Sorry I missed that you added async - that may work instead of changing to promise format in my above post.

However, did you restart node-red after saving changes?

I use the command

sudo systemctl restart nodered.service

After making any changes

I tried to add something like this after testing it on VSCode

            if (typeof this.client.authenticate() !== 'undefined' && this.client.authenticate() !== null) 
                {
                }
            else  
            {
            this.log('⛔️ Object null ');
            }

But I'm getting

[error] Error: Authentication failed: received 422 Unprocessable Entity

I really don't have any skills in JS so I'm not sure this was the good way to add something that can handle a null object.

client.authenticate is an async function (I pointed that out several posts above). You need to use await or use promise then/catch as I demonstrated in my previous post.