'Javascript recursive promise is firing the catch() twice, having a hard time following

I wrote a function in Discord.js v13 that expects user input. If the input is invalid, it needs to re-prompt them again, and the original promise should no longer be considered.

If the user takes longer than timeoutSeconds, then it throws an InputTimeout error.

What happens in practice, which I don't understand, is this:

  1. getResponse(...) is executed
  2. User is prompted for input
  3. User supplies invalid input
  4. User is informed of invalid input, and is re-prompted
  5. If no input is supplied, an InputTimeout error is thrown from the first request and the second request

What I wish to happen is:

  1. getResponse(...) is executed
  2. User is prompted for input
  3. User supplies invalid input
  4. User is informed of invalid input, and is re-prompted
  5. If no input is supplied, an InputTimeout error is thrown for the new request only

Here is what I think is happening... The second prompt is throwing the InputTimeout error which is causing the catch() handler from the first Promise to fire off. If that's true, then I am confused, because I thought if then() runs, then it's because no error was thrown. So why is catch() catching errors from the then() function? Should put catch() first, before then()? Is this an issue with the order I've chained the functions?

async getResponse(user, timeoutSeconds, channel = null) {
        if (channel == null) {
            const msg = await user.send(this.promptMessage);
            channel = msg.channel;
        }
        return await channel
            .awaitMessages({
                max: 1,
                time: timeoutSeconds * 1000,
                errors: ["time"],
            })
            .then(async (messages) => {
                if (messages.size == 1) {
                    const message = messages.values().next().value;
                    if (this.validator(message)) {
                        await user.send(this.successMessage);
                        return this.formatter(message);
                    } else {
                        await user.send(this.errorMessage);
                        await this.getResponse(
                            user,
                            timeoutSeconds,
                            channel
                        );
                    }
                }
            })
            .catch(async (e) => {
                console.error(e);
                await channel.send(this.timeoutMessage);
                throw new InputTimeout();
            });
    }


Solution 1:[1]

The issue was in fact the order of the .then() and .catch(). Since I was only concerned with catching errors from the awaitMessages() function, I should've chained it to that first, followed by the .then().

By putting catch() after then(), I was catching errors from my then() function.

Doh!

New solution, which also gets rid of .then() and .catch() as suggested by community:
try {
    let messages = await channel.awaitMessages({
        max: 1,
        time: timeoutSeconds * 1000,
        errors: ["time"],
    });
    messages = Array.from(messages.values());
    if (messages.length == 1) {
        const message = messages[0];
        if (message.author.bot) return;
        if (this.validator(message)) {
            await user.send(this.successMessage);
            return this.formatter(message);
        } else {
            await user.send(this.errorMessage);
            return this.getResponse( // note I removed "await" here to prevent nested errors
                user,
                timeoutSeconds,
                channel
            );
        }
    }
} catch (e) {
    console.error(e);
    await channel.send(this.timeoutMessage);
    throw new InputTimeout();
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1