skip to Main Content

I have a photoController.js file that has a "create" function that gets called when a post request to url: /photoCreate is triggered.The function then runs a python script with face detection for means of 2FA authentication. If the script returns TRUE I want to send a 200OK request and 500 status if it returns false, I will later receive these responses in my android studio app to either let the user through or deny them. I am encountering a "[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" error. The error appears on line return res.status(500).json({
I’ve been busting my head over this for like 5 hours now and I feel like im missing something trivial. Help is gladly appreciated.

create: function (req, res) {
    var photo = new PhotoModel({
        path: "/images/" + req.file.filename,
        postedBy: req.session.userId,
    });

    fs.access(`public/images/${req.session.userId}/${req.session.userId}.sav`, fs.constants.F_OK, (err) => {
        if (err) {
            const createModelScript = spawn('python', ['python/faceCreateModel.py', `public/images/${req.session.userId}`, "python/faces_false", `public/images/${req.session.userId}/${req.session.userId}`]);
            createModelScript.stdout.on('data', (data) => {
                // Process and handle the output from the Python script
                const result = data.toString();
                console.log(result);
            });
            createModelScript.stderr.on('data', (data) => {
                const error = data.toString();
                console.log(error);
            });
        } else {
            const files = fs.readdirSync(path.join('public/images', req.session.userId));
            const sortedFiles = files.map((filename) => {
                const filePath = path.join('public/images', req.session.userId, filename);
                const stat = fs.statSync(filePath);
                return {
                    filename,
                    createdAt: stat.birthtimeMs,
                };
            }).sort((a, b) => b.createdAt - a.createdAt);
            const recentImage = sortedFiles[0].filename;
            const faceTestScript = spawn('python', ['python/faceTestImage.py', `public/images/${req.session.userId}/${req.session.userId}.sav`, `public/images/${req.session.userId}/${recentImage}`]);
            faceTestScript.stdout.on('data', (data) => {
                const result = data.toString();
                console.log(result);
                if (result.trim() === 'true') {
                    return res.status(200).json({
                        message: 'Face test passed successfully'
                    });
                } else {
                    return res.status(400).json({
                        message: 'Face test failed'
                    });
                }
            });
            faceTestScript.stderr.on('data', (data) => {
                const error = data.toString();
                console.log(error);
ERROR HERE --->> ** return res.status(500).json({**
                    message: 'Error during face test',
                    error: error
                });
            });
        }
    })

    photo.save(function (err, photo) {
        if (err) {
            return res.status(500).json({
                message: 'Error when creating photo',
                error: err
            });
        }

        return res.status(201).json(photo);
        //return res.redirect('/photos');
    });
},

I’ve tried removing every single res.status statement but the ones that are sent if the script returns true or false but that didn’t work.Tried also moving the res.status to the photo.save function with if statements but that doesn’t work. Mind you this works perfectly fine if I remove the 2 result.status(200) and (500) statements but as soon as I added those I got that error.

2

Answers


  1. Chosen as BEST ANSWER

    I managed to fix this by creating a flag testPassed that gets updated accordingly and sending a single response at the end. The problem was that the function stdout.on() was causing the .send to be sent multiple times because it's essentially like a while loop until the script finishes. Im beating myself over such a mistake and hours wasted.I am now sending the response in the on('close') function which is only called once when the script finishes.

    create: function (req, res) {
            var photo = new PhotoModel({
                path: "/images/" + req.file.filename,
                postedBy: req.session.userId,
            });
            
            var testPassed = false;
    
            fs.access(`public/images/${req.session.userId}/${req.session.userId}.sav`, fs.constants.F_OK, (err) => {
                if (err) {
                    const createModelScript = spawn('python', ['python/faceCreateModel.py', `public/images/${req.session.userId}`, "python/faces_false", `public/images/${req.session.userId}/${req.session.userId}`]);
                    createModelScript.stdout.on('data', (data) => {
                        // Process and handle the output from the Python script
                        const result = data.toString();
                        console.log(result);
                    });
                    createModelScript.stderr.on('data', (data) => {
                        const error = data.toString();
                        console.log(error);
                    });
                } else {
                    const files = fs.readdirSync(path.join('public/images', req.session.userId));
                    const sortedFiles = files.map((filename) => {
                        const filePath = path.join('public/images', req.session.userId, filename);
                        const stat = fs.statSync(filePath);
                        return {
                            filename,
                            createdAt: stat.birthtimeMs,
                        };
                    }).sort((a, b) => b.createdAt - a.createdAt);
                    const recentImage = sortedFiles[0].filename;
                    const faceTestScript = spawn('python', ['python/faceTestImage.py', `public/images/${req.session.userId}/${req.session.userId}.sav`, `public/images/${req.session.userId}/${recentImage}`]);
                    faceTestScript.stdout.on('data', (data) => {
                        const result = data.toString();
                        if (result.trim() === 'TRUE') {
                            testPassed = true;
                        } else {
                            testPassed = false;
                        }
                    });
                    faceTestScript.stderr.on('data', (data) => {
                        const error = data.toString();
                        console.log(error);
                    });
                    faceTestScript.on('close', () => {
                    if (testPassed) {
                        console.log("true");
                        return res.status(201).json({
                            photo,
                            testPassed: true
                        });
                    } else {
                        console.log("false");
                        return res.status(500).json({
                            message: 'Test failed',
                            error: err,
                            testPassed: false
                        });
                    }
                });
                }
            })
    

  2. Looks like you’re trying to send a response to the client after a response has already been sent.
    In your code you’re trying to send multiple responses within the callback functions of createModelScript.stdout.on and faceTestScript.stdout.on.

    You should ensure that you only send a response once. here’s an example:

    create: function (req, res) {
        var photo = new PhotoModel({
            path: "/images/" + req.file.filename,
            postedBy: req.session.userId,
        });
    
        fs.access(`public/images/${req.session.userId}/${req.session.userId}.sav`, fs.constants.F_OK, (err) => {
            if (err) {
                const createModelScript = spawn('python', ['python/faceCreateModel.py', `public/images/${req.session.userId}`, "python/faces_false", `public/images/${req.session.userId}/${req.session.userId}`]);
                createModelScript.stdout.on('data', (data) => {
                    // Process and handle the output from the Python script
                    const result = data.toString();
                    console.log(result);
                });
                createModelScript.stderr.on('data', (data) => {
                    const error = data.toString();
                    console.log(error);
                });
            } else {
                const files = fs.readdirSync(path.join('public/images', req.session.userId));
                const sortedFiles = files.map((filename) => {
                    const filePath = path.join('public/images', req.session.userId, filename);
                    const stat = fs.statSync(filePath);
                    return {
                        filename,
                        createdAt: stat.birthtimeMs,
                    };
                }).sort((a, b) => b.createdAt - a.createdAt);
                const recentImage = sortedFiles[0].filename;
                const faceTestScript = spawn('python', ['python/faceTestImage.py', `public/images/${req.session.userId}/${req.session.userId}.sav`, `public/images/${req.session.userId}/${recentImage}`]);
                faceTestScript.stdout.on('data', (data) => {
                    const result = data.toString();
                    console.log(result);
                    if (result.trim() === 'true') {
                        // If face test passed, save photo and send the response
                        photo.save(function (err, savedPhoto) {
                            if (err) {
                                return res.status(500).json({
                                    message: 'Error when creating photo',
                                    error: err
                                });
                            }
                            return res.status(200).json({
                                message: 'Face test passed successfully',
                                photo: savedPhoto
                            });
                        });
                    } else {
                        // If face test failed, send the response directly
                        return res.status(400).json({
                            message: 'Face test failed'
                        });
                    }
                });
                faceTestScript.stderr.on('data', (data) => {
                    const error = data.toString();
                    console.log(error);
                    return res.status(500).json({
                        message: 'Error during face test',
                        error: error
                    });
                });
            }
        });
    },
    

    Now the response is sent only once for each scenario. If the face test passes, the photo is saved, and the response is sent within the callback of photo.save(). If the face test fails, the response is sent directly within the if (result.trim() === 'true') block. Similarly, if there’s an error during the face test, the response is sent within the faceTestScript.stderr.on callback.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search