Error: VM Exception while processing transaction: revert

  • for Error: VM Exception while processing transaction: revert
  •  or Error: VM Exception while processing transaction: invalid opcode
  •  or Error: VM Exception while processing transaction: out of gas

0x has released sol-trace, which helps immensely. Learn more about it here

👉 @0x/sol-trace 👈

I got this error! What do I do????

Unfortunately, this error is incredibly generic and unhelpful. You have two options:

Option 1


  • ☑ Lie Down
  • ☑ Try Not to Cry
  • ☑ Cry a Lot

Option 2

Don't Panic

  • ☑ This error means your transaction failed.
  • ☑ It's very generic, so don't expect to find a copy-paste solution on google.
  • ☑ Let's debug it!

I get out of gas errors!

You're either not sending enough gas or are hitting the block gas limit. Estimate your gas requirement with estimateGas(). If it's about equal to the block gas limit, it either requires too much gas to fit in a block or is using up all of the gas you provide it because of an error, which we can debug below.

Does your contract just need too much gas to deploy? Try splitting your deployment into multiple transactions.

If you're using solc directly, use the --optimize --runs=1 parameters via the docs.

If you're using truffle, configure the optimizer in truffle.js with solc: { optimizer: { enabled: true, runs: 200 } }

Are you using a helper library like web3.js or a library like truffle that uses web3.js? Each individual call may have a self-imposed gas limit on it; make sure your truffle.js config sets a gas limit vaguely equal to the chain you're deploying to.

Using a library like web3.js directly? It may not be estimating gas for you at all. Try the brute force approach below to see if that's the issue.

If you're running on a test net, you can employ the "brute force" approach of "just send as much gas as possible" and see if that was your issue. Try myContract.myMethod(..., { gas: 8000000 }) and see if it works. If it does, your gas estimation or gas limit is definitely wrong somewhere. If that didn't fix your problem, you're either going above the block gas limit or running into one of the problems below.

Are you using hella strings? Honestly you might just be using too much gas; strings can be silenty very expensive.

This occurs when I deploy!

"revert" or "invalid opcode"

You're probably failing a require() block in the code path executed by your constructor. Make sure all of your input arguments are absolutely correct!

Deploying a crowdsale? Make sure your opening and closing times pass the required checks. Your start time needs to be at least a few blocks ahead of when you deploy, otherwise you'll fail the require(openingTime > block.timestamp) condition because the time your contract is actually deployed is pretty random.

You could also be running into authorization issues, discussed below.

This occurs when I try to send a transaction!


The bad news is that literally anything could have gone wrong here. All it means is that a REVERT opcode was issued somewhere along the way. No, you don't get a stack trace. Using truffle or web3.js? No, you don't get to know which transaction caused the issue, since those are handled asynchronously and the stack traces have 0 context and are completely useless. Oh, you're using truffle? You get a special, minified, entirely garbage stack trace that is of no use to anyone in the conceivable universe.

The good news is that it was probably a require() block somewhere, so you can start tracking that down.

If you're running a suite of transactions and don't know which transaction caused the error, either step-though each rpc call as it happens or do the 'ole printf debugging approach where you put console.log('1.') in between each rpc call, allowing you to see which one fails.

Now that you've narrowed down which call is the problem, mentally evaluate the code, given the inputs you're sending to the contract. Go over things like:

  • input validation — is what I'm sending what the contract expects?
  • access control — am I signing this transaction from the correct account? do I have the permissions to do this at all?
  • math — does all of the math work out?
  • access control — seriously, make sure your contract has the correct token allowance

You can check to see how much gas was consumed to estimate where in the code path your transaction failed. Only used ~21,000 gas? You probably hit a modifier like onlyOwner that stopped your transaction in its tracks.

You can also just start commenting-out code that might be the problem and see when the execution actually succeeds! Binary search is your friend here 😉.

Are you working with ether or tokens? You may have mixed up your units somewhere. Unless there's a specific reason, all storage and math relating to currencies should be done in the smallest amount of that currency. This means your contract should only accept wei in its arguments. If you're doing token math, you might have mixed up 1 TKNbit with 1 TKN. A TKNbit is the smallest unit of your token; if your decimals are 18, 1 TKN = 10^18 TKNbits.

I'm working on a crowdsale!

Well first, if you can't solve this one yourself you probably shouldn't be working on a crowdsale. don't @ me

That said, let's debug it! Is the buyTokens() function failing? Your problem is probably one of these:

  • you didn't send any money to the crowdsale
  • you're purchasing for a null address
  • your token address is null
  • Crowdsale.sol: your crowdsale doesn't own any tokens
  • MintableCrowdsale.sol: your crowdsale can't mint token because minting is finished or your token doesn't conform to MintableToken or it does not pass hasMintingPermission
  • AllowanceCrowdsale.sol: your crowdsale doesn't have an allowance to the token
  • FinalizableCrowdsale.sol: your crowdsale is finalized already
  • RefundableCrowdsale.sol: your wallet is not a RefundVault
  • your destination wallet has a fallback function that rejects transfers

"invalid opcode"

You're not any better off with this error, so consider taking "Option 1: Panic" above. This error means that an invlid opcode was produced in your contract, and the EVM has no idea what to do with it. So it bails, and it bails hard.

This error could be from an assert() failure within your code path; check those first.

I'm working with arrays!

Make sure you're not accessing an array out-of-bounds! If you're using dynamically sized arrays, a common mistake is to myArray[newIndex] = value before you've manually allocated that space by doing myArray.length++. Using myArray.push(value) does this behind the scenes.

None of that solved my problem!

Well, the only thing someone else is going to do is look over your code and do the exact same thing we went through above, but a second pair of eyes is always helpful.

If you're going to ask for help in a forum, you must include your entire source code, including the script by which you call your contracts. Don't cherry-pick things you think are the problem; just post the code!

And for the love of all that is holy, don't just say "please help???" and never respond to questions from community members asking for clarification.

Will this disaster of a development experience get any better?

Yup! In solidity 0.4.22, the ability to specify an error message was added. It looks like require(something, "something didn't happen!").

For now, this extra return information isn't really supported by tooling like web3.js and friends. So, uh, just sit tight there, yeah? Or maybe open a PR against your favorite libraries!

Also, follow the EIP 1066: Status Codes as it develops.

Want to know more about how Ethereum actually works?

  1. First, Getting up to Speed on Ethereum
  2. then read the whitepaper.