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
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.
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
andfaceTestScript.stdout.on
.You should ensure that you only send a response once. here’s an example:
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 theif (result.trim() === 'true')
block. Similarly, if there’s an error during the face test, the response is sent within thefaceTestScript.stderr.on
callback.