Skip to content

Handling errors

We've already briefly touched handling errors in dispatchers in the Getting Started section, but let's dive a bit deeper.

Registering a handler

An error handler for a Dispatcher is simply a function that is called whenever an error is thrown inside one of the handlers.

For convenience, that function has access to the error itself, the parsed update and the state (if applicable):

ts
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

dp.onError(async (error, update, state) => {
    if (update.name === 'new_message') {
        await update.data.replyText(`Error: ${error.message}`)

        return true
    }

    return false
})
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

dp.onError(async (error, update, state) => {
    if (update.name === 'new_message') {
        await update.data.replyText(`Error: ${error.message}`)

        return true
    }

    return false
})

Error handler function is expected to return boolean indicating whether the update was handled. If it was not, it will be propagated to Client.

update is an object that contains 2 fields: name and data.

name is simply update name (new_message, edit_message, etc.; see Handlers), and data is the respective object.

What errors are not handled

Errors inside Raw update handlers are not handled by the error handler. Any other errors within the same dispatcher (both handlers and filters) are handled by it.

Propagation

To Client

If the error handler is not registered, throws an error or returns false, the error is propagated to Client's error handler. Obviously, in Client's error handler you won't have access to the update that caused this error:

ts
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

tg.onError((err) => {
    // will be called since there's no `dp.onError`
    console.log(err)
})
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

tg.onError((err) => {
    // will be called since there's no `dp.onError`
    console.log(err)
})

Within the Dispatcher

When an error is thrown by one of the handlers, propagation within this dispatcher stops (the same way as if it returned StopPropagation):

ts
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

dp.onNewMessage(
  async (msg) => {
    // will not reach
  }
)
dp.onNewMessage(
  filters.command('do_stuff'),
  async (msg) => {
    throw new Error('Some error')
  }
)

dp.onNewMessage(
  async (msg) => {
    // will not reach
  }
)

To parent/children

Errors are not propagated to parent dispatcher or to any of the children dispatchers:

ts
const dp = new Dispatcher(tg)
const dp1 = new Dispatcher()
const dp2 = new Dispatcher()

dp.addChild(dp1)
dp1.addChild(dp2)

// dp --child--> dp1 --child--> dp2

dp.onError(() => console.log('DP caught error'))
dp1.onError(() => console.log('DP1 caught error'))
dp2.onError(() => console.log('DP2 caught error'))

dp1.onNewMessage(() => { throw new Error() })

// Only "DP1 caught error" will ever be printed
const dp = new Dispatcher(tg)
const dp1 = new Dispatcher()
const dp2 = new Dispatcher()

dp.addChild(dp1)
dp1.addChild(dp2)

// dp --child--> dp1 --child--> dp2

dp.onError(() => console.log('DP caught error'))
dp1.onError(() => console.log('DP1 caught error'))
dp2.onError(() => console.log('DP2 caught error'))

dp1.onNewMessage(() => { throw new Error() })

// Only "DP1 caught error" will ever be printed

However, if you need that behaviour, you can use propagateErrorToParent:

ts
const dp = new Dispatcher(tg)
const dp1 = new Dispatcher()
const dp2 = new Dispatcher()

dp.addChild(dp1)
dp1.addChild(dp2)

// dp --child--> dp1 --child--> dp2

dp.onError(() => console.log('DP caught error'))
dp1.onError(() => {
  console.log('DP1 caught error')

  return dp1.propagateErrorToParent(...arguments)
})
dp2.onError(() => console.log('DP2 caught error'))

dp1.onNewMessage(() => { throw new Error() })

// "DP1 caught error" and "DP caught error" will be printed for each new message
const dp = new Dispatcher(tg)
const dp1 = new Dispatcher()
const dp2 = new Dispatcher()

dp.addChild(dp1)
dp1.addChild(dp2)

// dp --child--> dp1 --child--> dp2

dp.onError(() => console.log('DP caught error'))
dp1.onError(() => {
  console.log('DP1 caught error')

  return dp1.propagateErrorToParent(...arguments)
})
dp2.onError(() => console.log('DP2 caught error'))

dp1.onNewMessage(() => { throw new Error() })

// "DP1 caught error" and "DP caught error" will be printed for each new message

mtcute is not affiliated with Telegram.