ethers.js: Unhandled promise rejections

Hi,

I’ve noticed that issues #180 and #185 are due to unhandled promise rejections, so I analyzed the whole codebase (manually and using this eslint plugin) to find the root of these problems.

Note that this problems may be ignorable for now, but in the future node will kill the entire app if a promise rejection is unhandled. See: https://nodejs.org/dist/latest-v8.x/docs/api/deprecations.html#deprecations_dep0018_unhandled_promise_rejections

I identified the following Promises with a then() and no catch(), nor being returned:

Most of them are present in places where HTTP requests are done, and their failures are not managed.

There’s also these cases, where a unhandledRejection handled is set, but you may want to fix them anyway to avoid the bad patter:

Finally, these functions look suspicious. They have a try {} catch{} and a throw inside a then(). Not sure which was the intention, but the throw from the then() never gets to the catch clause`:

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 1
  • Comments: 22 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I need to increase the timeout on some test cases. I think I’m being throttled more by Travis CI since I’ve added more target platforms to test. I’ll get to that shortly.

Getting back to that example, if you want the promise returned by JsonRpcSigner.prototype.sendTransaction to poll for the transaction until it’s found or a an error happens, you should only change the ‘check()’ function like this:

function check() {
  provider.getTransaction(hash).then(function(transaction) {
      if (!transaction) {
          setTimeout(check, 1000);
          return;
      }
      transaction.wait = function() {
          return provider.waitForTransaction(hash);
      };
      resolve(transaction);
  })
  .catch(reject); // <------ This line is new
}

If you can clarify if this is the intended behavior, I’m happy to submit a PR fixing all the cases.

Here’s another I’ve seen in the logs

3|sentry   | (node:70836) UnhandledPromiseRejectionWarning: TypeError: name.toLowerCase is not a function
3|sentry   |     at Object.namehash (/home/oraclize/sentry/node_modules/ethers/utils/namehash.js:12:17)
3|sentry   |     at FallbackProvider.resolveName (/home/oraclize/sentry/node_modules/ethers/providers/provider.js:946:26)
3|sentry   |     at FallbackProvider.getBalance (/home/oraclize/sentry/node_modules/ethers/providers/provider.js:709:17)

Oh, I see. I can definitely see how that could leak memory for a system tracking the promises.

Sorry for the delay, I will fix this in both the 3.0 and 4.0 branch tomorrow.

I will be getting to this today. 😃

This is also happening to me in Node! Here is my code! It works perfectly for 3-4 minutes and then I get this error: ` (node:7862) UnhandledPromiseRejectionWarning: Error: invalid json response at exports.XMLHttpRequest.request.onreadystatechange (/home/appo/node_modules/ethers/providers/provider.js:606:33) at exports.XMLHttpRequest.dispatchEvent (/home/appo/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:591:25) at setState (/home/appo/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:610:14) at exports.XMLHttpRequest.handleError (/home/appo/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:532:5) at ClientRequest.errorHandler (/home/appo/node_modules/xmlhttprequest/lib/XMLHttpRequest.js:459:14) at ClientRequest.emit (events.js:182:13) at Socket.socketErrorListener (_http_client.js:382:9) at Socket.emit (events.js:182:13) at emitErrorNT (internal/streams/destroy.js:82:8) at emitErrorAndCloseNT (internal/streams/destroy.js:50:3) at process._tickCallback (internal/process/next_tick.js:63:19)

`

` try { const ethers = require(‘ethers’)

const provider = new ethers.providers.JsonRpcProvider(HTTP://127.0.0.1:8588)

const Web3 = require(‘web3’)

const web3 = new Web3(new Web3.providers.HttpProvider(HTTP://127.0.0.1:8588))

provider.on( block, async (blockNumber) => { console.log(Enter provider on block ${blockNumber}) /* If the block number is less than 2 (0 or 1), then do not excute the code below */ if (blockNumber < 2) { return }

  /*
    Getting the block info on the block which was just created and the previous one
 */
  const [
    currentBlockInfo,
    previousBlockNumber
  ] = await Promise.all([
    web3.eth.getBlock(blockNumber),
    web3.eth.getBlock(blockNumber - 1)
  ])

  /*
    Date is given in seconds from epoch so changing seconds to milliseconds
  */
  const timeCurrentBlockCreated = new Date(currentBlockInfo.timestamp * 1000)
  const timePreviousBlockCreated = new Date(previousBlockNumber.timestamp * 1000)

  /*
    Getting the amount of seconds elapsed since the last block was created
  */
  const durationOfBlockInSeconds = (
    timeCurrentBlockCreated.getTime() - timePreviousBlockCreated.getTime() // returns the time elapsed in milliseconds, therefore dividing this result by 1,000
  ) / 1000

  /*
    currentBlockInfo.transactions is an array of all the transactionId's which was proccess in the block
  */
  const numberOfProccessedTransactions = currentBlockInfo.transactions.length

  /*
    Getting the average number of transactions occuring per second
  */
  const averageNumberOfTransactionsPerSecond = numberOfProccessedTransactions / durationOfBlockInSeconds

  const {
    totalDifficulty,
    difficulty,
    gasUsed,
    gasLimit
  } = currentBlockInfo

  console.log({
    averageNumberOfTransactionsPerSecond,
    difficultyCurrentBlock: difficulty,
    difficultyTotal: totalDifficulty, // integer of the total difficulty of the chain until this block
    numberOfProccessedTransactions,
    durationOfBlockInSeconds,
    blockNumber,
    gasLimit,
    gasUsed
  })
}

)

} catch (err) { console.log({ success: false, err }) } `