1

This code block always gives me the error -

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode)

module.exports = (app, spotifyAPI) => {

    app.get('/api/search', requireLogin, async (req, res) => {

        const URI_BASE = keys.ComputerVisionEndpoint + 'vision/v3.0/analyze';
        const imageUrl = "https://upload.wikimedia.org/wikipedia/commons/3/3c/Shaki_waterfall.jpg"; // will be sent as req body
        var results;

        // making API call to microsoft cognitive services API 
        try {
            results = await axios({
                method: 'post',
                url: URI_BASE,
                headers: {
                    'Content-Type': 'application/json',
                    'Ocp-Apim-Subscription-Key' : keys.ComputerVision
                }, 
                params: {
                    'visualFeatures': 'Tags',
                    'details': '',
                    'language': 'en'
                },
                data: {
                "url": imageUrl,
                }
            });
        } catch (err) {
            res.status(400).send(err);
        }

        // remove the common ones - indoor, outdoor, ground, wall, person, woman, man, ceiling, floor
        const to_filter = results['data']['tags'];
        _.remove(to_filter, (item) => {
            return (item.name === 'indoor' || item.name === 'outdoor' || item.name === 'ground' || item.name === 'wall'
                || item.name === 'person' || item.name === 'woman' || item.name === 'man' || item.name === 'ceiling'
                || item.name === 'floor'
            );
        });

        // creating playlist and getting the playlist ID
        var playlist_id;
        try {
            playlist_id = create_playlist(req, res, spotifyAPI);
        } catch(err) {
            if (err['statusCode'] === 401) {
                req.logout();
                return res.redirect('/');
            }
            else {
                return res.status(400).send(err);
            }
        }

        // searching for relevant songs and adding them to the playlist
        try {
            search_and_add(req, res, spotifyAPI, to_filter, playlist_id);
        } catch (err) {
            if (err['statusCode'] === 401) {
                req.logout();
                res.redirect('/');
            }
            else {
                res.status(400).send(err);
            }
        }

    });
}

create_playlist = async (req, res, spotifyAPI) => {
    try {
        const playlist = await spotifyAPI.createPlaylist(req.user.id, 'TuneIn Playlist', { 'public' : false });
        const playlist_id = playlist['body']['id'];
        return playlist_id;
    } catch (err) {
        throw err;
    }
}

search_and_add = async (req, res, spotifyAPI, to_filter, playlist_id) => {
    _.map(to_filter, async (tag) => {
        try {
            const song_details = await spotifyAPI.searchTracks(tag.name, { limit: 1 });
            const song_uri = song_details.body['tracks']['items'][0]['uri'];
            console.log(song_uri);
        } catch (err) {
            throw err;
        }
    });
    // figure out where to re direct user 
}

Could someone please help me out?

Uddhav Bhagat
  • 63
  • 1
  • 2
  • 6

1 Answers1

0

You're not checking the return value of create_playlist.

Imagine an error is caught in create_playlist, and err['statusCode'] !== 401, then you'll go to res.status(400).send(err).

Then you'll go to search_and_add, because you just sent the response, but didn't tell your code to abort (using try catch in your "main" function). Now, if you have an error, and it's code is different to 401, then you'll send the response again.

Sending twice a response for a single call results in this message :

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Be careful with your responses, and abort the entire call whenever you use res.status(4xx).send(err), because you won't be able to send a successful response to the client after that.

Dharman
  • 30,962
  • 25
  • 85
  • 135
  • Thank you, that helps! But, how would I abort? Where should I try catch to abort? Sorry, I'm new to this – Uddhav Bhagat Jul 13 '20 at 11:31
  • One way you could catch it is by including your function calls (create_playlist and search_and_add), and changing your catches in both create_playlist and search_and_add to just throw your err, typically, it would give : `.catch(err) {throw err}` and, in your main function, change your catch callback to the one you had before for create_playlist and search_and_add. This way, if create_playlist fails, you will get the error object in your main function, so if you catch an error, the try will stop, because you threw an error, and will just send the error response – Paskal Arzel Jul 13 '20 at 11:45
  • I tried doing that. But, now it always gives me this error - UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 7) – Uddhav Bhagat Jul 13 '20 at 12:05
  • Would you know how I can get rid of this? – Uddhav Bhagat Jul 13 '20 at 12:06
  • Could you paste your new code here (or send a link containing it) so I can check if everything is fine ? – Paskal Arzel Jul 13 '20 at 12:13
  • I have updated the post with the new code as per what you told me! Thank you so much I appreciate it – Uddhav Bhagat Jul 13 '20 at 12:16
  • Took some time to notice that, but your function search_and_add calls a ._map function, so if you throw inside this ._map function, then you'll have to catch it in search_and_add again. If I'm right, it's not possible to do that, however, you could give an obviously wrong value to data with error, like -1 or NULL. Except this function, everything seems to be well-written, so you just need to find a way to handle the bad cases for search_and_add – Paskal Arzel Jul 13 '20 at 12:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/217740/discussion-between-uddhav-bhagat-and-paskal-arzel). – Uddhav Bhagat Jul 13 '20 at 12:55