grammyjs / runner

Scale bots that use long polling to receive updates.
https://grammy.dev/plugins/runner
MIT License
19 stars 4 forks source link

sequentialize + apiThrottler causes errors to slip through and kill the Node process #5

Closed slavafomin closed 3 years ago

slavafomin commented 3 years ago

Hello!

Today, I've encountered a weird behavior when sequentialize is used alongside the apiThrottler.

Run the code below and start to click on the provided menu button as fast as possible. When the requests will start to run very fast the API error (400: Bad Request: message is not modified) will slip through the catch handler and will kill the Node process.

import { Menu } from '@grammyjs/menu';
import { Bot, GrammyError, HttpError } from 'grammy';
import { run, sequentialize } from '@grammyjs/runner';
import { apiThrottler } from '@grammyjs/transformer-throttler';

void (async function main() {

  const bot = new Bot('<token>');

  bot.use(sequentialize(
    context => String(context.chat.id)
  ));

  bot.api.config.use(apiThrottler());

  const menu = (new Menu('my-menu-identifier')
    .text('A', (ctx) => ctx.editMessageText(
      'You pressed A!', { reply_markup: menu })
    )
  );

  bot.use(menu);

  bot.command('start', async (ctx) => {
    // Send the menu:
    await ctx.reply('Check out this menu:', { reply_markup: menu });
  });

  bot.catch((err) => {
    const ctx = err.ctx;
    console.error(`Error while handling update ${ctx.update.update_id}:`);
    const e = err.error;
    if (e instanceof GrammyError) {
      console.error('Error in request:', e.description);
    } else if (e instanceof HttpError) {
      console.error('Could not contact Telegram:', e);
    } else {
      console.error('Unknown error:', e);
    }
  });

  run(bot);

})();

All the code blocks are taken mostly from the documentation directly.

KnorpelSenf commented 3 years ago

My intuition is that one of the two plugins (probably sequentialize) does not handle errors correctly, and the other one (probably transformer-throttler) causes the issue to appear.

KnorpelSenf commented 3 years ago

@slavafomin can you also reproduce this without the menu plugin?

KnightNiwrem commented 3 years ago

My intuition is that one of the two plugins (probably sequentialize) does not handle errors correctly, and the other one (probably transformer-throttler) causes the issue to appear.

Your intuition is correct. The error is reproducible without @grammyjs/transformer-throttler. The offending line appears to be https://github.com/grammyjs/runner/blob/main/src/sequentialize.ts#L68, and it does not seem like transformer-throttler helps or hinders the issue here.

In this line, p.chain could be task from a previous ctx object, and thus, can potentially be rejected/thrown. Attaching a .catch here, resolves the issue.

slavafomin commented 3 years ago

it does not seem like transformer-throttler helps or hinders the issue here

Strangely, I can reproduce the error only when both plugins are present.

KnightNiwrem commented 3 years ago

it does not seem like transformer-throttler helps or hinders the issue here

Strangely, I can reproduce the error only when both plugins are present.

import { Menu } from '@grammyjs/menu';
import { Bot, GrammyError, HttpError } from 'grammy';
import { run, sequentialize } from '@grammyjs/runner';

void (async function main() {

  const bot = new Bot('<token>');

  bot.use(sequentialize(
    context => String(context.chat!.id)
  ));

  const menu: any = (new Menu('my-menu-identifier')
    .text('A', (ctx) => ctx.editMessageText(
      'You pressed A!', { reply_markup: menu })
    )
  );

  bot.use(menu);

  bot.command('start', async (ctx) => {
    // Send the menu:
    await ctx.reply('Check out this menu:', { reply_markup: menu });
  });

  bot.catch((err) => {
    const ctx = err.ctx;
    console.error(`Error while handling update ${ctx.update.update_id}:`);
    const e = err.error;
    if (e instanceof GrammyError) {
      console.error('Error in request:', e.description);
    } else if (e instanceof HttpError) {
      console.error('Could not contact Telegram:', e);
    } else {
      console.error('Unknown error:', e);
    }
  });

  run(bot);

})();
✨  Done in 1.56s.
Error while handling update 908291894:
Error in request: Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message
node:internal/process/promises:246
          triggerUncaughtException(err, true /* fromPromise */);
          ^

GrammyError: Call to 'editMessageText' failed! (400: Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message)
    at ApiClient.callApi (/grammyjs/grammy-test/node_modules/grammy/out/core/client.js:110:19)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:71:13
    at async /grammyjs/grammy-test/node_modules/@grammyjs/menu/out/menu.js:553:17
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:66:9
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:71:13
    at async Promise.all (index 0)
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:71:13
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:66:9
    at async /grammyjs/grammy-test/node_modules/grammy/out/composer.js:66:9 {
  method: 'editMessageText',
  payload: {
    chat_id: 41284431,
    message_id: 4583,
    text: 'You pressed A!',
    reply_markup: {
      inline_keyboard: [
        [
          {
            callback_data: 'my-menu-identifier/0/0//\x00\r)l',
            text: 'A'
          }
        ]
      ]
    }
  },
  ok: false,
  error_code: 400,
  description: 'Bad Request: message is not modified: specified new message content and reply markup are exactly the same as a current content and reply markup of the message',
  parameters: {}
}
KnorpelSenf commented 3 years ago

The fix for this issue was released in version 1.0.3.

slavafomin commented 2 years ago

Thank you guys, that was fast :)

KnorpelSenf commented 2 years ago

Can you confirm that it works? :)