Nodered keeps restarting with processTicksAndRejections error

Yes that's what I've edited the last time, but like I said it returned me the error

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

So I tried to add something to handle an undefined or null object in the async function like this


    async 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});
                }
           

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


    }

Then i get

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

During the Nodered restart

Sorry, I was trying to guide you to an answer
I will be more specific. This code will never work....

Delete it and try again.

Also, could you please try the promise version I wrote here....

Exactly as written.

So after testing the code I modify the node status for .then() to be accurate, like this :

    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}))
        }
    }

Nodered is deployed well, flows are started, I get informations from the API as usual. Now I have to make it run for a while and see if I still get crash from timeout errors.

Again @Steve-Mcl a big thank you for your help and your time on this subject, it means a lot to me.

Hi, so after two days of running, unfortunately I still get the exact same crashes from time to time

[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)

So if you look in the file surepet.js you should find a call to .getState() and it probably doesnt have a catch block or is not handling the timeout correctly.

Can you paste exactly the full content of your surepet.js as it is right now.

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SurePetCareClient = void 0;
const node_fetch_1 = __importDefault(require("node-fetch"));
class SurePetCareClient {
    constructor() {
        this.token = '';
        this.baseUrl = 'https://app.api.surehub.io';
        this.authenticate = async (email, password) => {
            const resp = await node_fetch_1.default(`${this.baseUrl}/api/auth/login`, {
                method: 'POST',
                body: JSON.stringify({ email_address: email, password, device_id: '.' }),
                headers: { 'Content-Type': 'application/json' },
            });
            if (!resp.ok) {
                switch (resp.status) {
                    case 401:
                        throw new Error('Authentication failed: invalid credentials');
                    default:
                        throw new Error(`Authentication failed: received ${resp.status} ${resp.statusText}`);
                }
            }
            const token = resp.headers.get('Authorization');
            if (token === null) {
                throw new Error('Authentication failed: no authorization token in response');
            }
            this.token = token;
        };
        this.getState = async () => {
            const resp = await node_fetch_1.default(`${this.baseUrl}/api/me/start`, {
                headers: { 'Content-Type': 'application/json', 'Authorization': this.token },
            });
            if (!resp.ok) {
                switch (resp.status) {
                    case 401:
                        throw new Error('Get state failed: unauthenticated');
                    default:
                        throw new Error(`Get state failed: received ${resp.status} ${resp.statusText}`);
                }
            }
            const { data } = await resp.json();
            return data;
        };
        this.getPets = async () => {
            const state = await this.getState();
            return state.pets;
        };
        this.getPetByName = async (name) => {
            const pets = await this.getPets();
            const pet = pets.find((p) => p.name.toLowerCase() === name.toLowerCase());
            if (!pet) {
                throw new Error('Pet not found');
            }
            return pet;
        };
        this.getPetById = async (id) => {
            const pets = await this.getPets();
            const pet = pets.find((p) => p.id === id);
            if (!pet) {
                throw new Error('Pet not found');
            }
            return pet;
        };
        this.setPetLocation = async (id, location, since = new Date()) => {
            const date = since.toISOString();
            const resp = await node_fetch_1.default(`${this.baseUrl}/api/pet/${id}/position`, {
                method: 'POST',
                body: JSON.stringify({ since: date, where: location }),
                headers: { 'Content-Type': 'application/json', 'Authorization': this.token },
            });
            if (!resp.ok) {
                switch (resp.status) {
                    case 401:
                        throw new Error('Set pet location failed failed: invalid credentials');
                    default:
                        throw new Error(`Set pet location failed failed: received ${resp.status} ${resp.statusText}`);
                }
            }
        };
    }
}
exports.SurePetCareClient = SurePetCareClient;

that is not the right file. I want to see where getState is called by the contrib node.

It will be file node_modules/node-red-contrib-surepet/surepet.js

The one in node_modules/node-red-contrib-surepet/surepet.js is the exact same one as /.node-red/node_modules/node-red-contrib-surepet/surepet.js

Then something is very wrong.

It should look like a node-red module. i.e. the first line will read module.exports = function(RED){ (what you pasted is a class file)

It should look like this: node-red-contrib-surepet/surepet.js at 548e3d50560bb9af4b4a38195c72e43495da02d0 ¡ jpwsutton/node-red-contrib-surepet ¡ GitHub

So just to be clear, the file at node_modules/node-red-contrib-surepet/surepet.js is the exact same one as /.node-red/node_modules/node-red-contrib-surepet/surepet.js

Here is the following :

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)
            })
        } else if(config.mode === "get_status"){
            this.surepet.client.getState().then((state) => {
                msg.payload = state;
                node.send(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();
        const node = this
        if (this.username && this.password){
             this.log("We have a username and password, attempting to authenticiate.")
            this.client.authenticate(this.username, this.password)
           .then(() => node.status({fill:"green",shape:"dot",text:"ConnectĂŠ."}))
           .catch(err => node.status({fill:"red",shape:"ring",text:err}))
        }
    }

    RED.nodes.registerType("surepet-credentials",SurePetCredentials,{
        credentials: {
            username: {type:"text"},
            password: {type:"password"}
        }
    });
}

There should not be one at ~/node_modules/node-red-contrib-surepet/surepet.js. If there is that probably means that you manually installed the node from your home folder instead of from ~/.node-red. You can safely ignore the one in ~/node_modules. The one in .node-red/node_modules is the one you need to adjust.

Thanks for the explanation, I was wondering why I only get this one at two differents folders, I will delete the useless one.

So for now the file at /.node-red/node_modules/node-red-contrib-surepet/surepet.js is the one I paste on my previous post.

@klose54

Try replacing that files content with the code below

Changes:

  1. Ensure all promise calls have a .catch block & that node.error() is called upon error (so a use can catch errors with the catch node
  2. Add "connect" msg support. This way, you can send a msg with a topic of "connect" to change (or reconnect) at runtime (pass the username and password in the msg or msg.payload
module.exports = function (RED) {
    "use strict"
    const SurePet = require('sure-pet-care-client');

    function SurePetNode(config) {
        RED.nodes.createNode(this, config);
        const node = this;
        node.surepet = RED.nodes.getNode(config.surepet);

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

            // special case, if `topic` === connect, then re-authenticate
            if (msg.topic === "connect") {
                node.surepet.client = new SurePet.SurePetCareClient();
                node.surepet.client.authenticate(msg.username || msg.payload?.username, msg.password || msg.payload?.password)
                .then(() => node.status({ fill: "green", shape: "dot", text: "Connected." }))
                .catch(err => {
                    node.error(err, msg)
                    node.status({ fill: "red", shape: "ring", text: err })
                })
                return
            }

            // normal operation
            if (config.mode === 'list_pets') {
                node.surepet.client.getPets().then((pets) => {
                    msg.payload = {
                        pets: pets
                    }
                    node.send(msg)
                }).catch((err) => {
                    node.error(err, msg);
                })
            } else if (config.mode === "get_status") {
                node.surepet.client.getState().then((state) => {
                    msg.payload = state;
                    node.send(msg);
                }).catch((err) => {
                    node.error(err, msg);
                })
            } 
        });
    }
    RED.nodes.registerType("surepet", SurePetNode);

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

    RED.nodes.registerType("surepet-credentials", SurePetCredentials, {
        credentials: {
            username: { type: "text" },
            password: { type: "password" }
        }
    });
}

@Steve-Mcl Your edits deployed well, API is answering. Let's see how things goes for some time. Thank you.

@Steve-Mcl

I think the problem is now solved thank to your last edit. This afternoon I did not have a single reboot of nodered but I get some error msg in when there was server time out.

image

Again, thanks a lot for your help. :slight_smile:

I will update the github with your code for other users after some more days of observation but things are getting really well.

1 Like

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