What Solidity try/catch actually catches

I have been looking into some edge cases around Solidity try/catch.

The main point is that try/catch only catches failures from the external call or contract creation expression itself. Some failures around that expression can still revert the caller and bypass the catch block entirely.

A few examples:

  • Reverts inside the external call can be caught.

  • Panic errors such as assert failures or arithmetic errors inside the external call can be caught with catch Panic(uint256).

  • Custom errors and unknown revert data can be handled with catch (bytes memory).

  • Errors while evaluating arguments before the call are not caught.

  • Errors while decoding return data may bypass the catch block.

  • Calls to addresses without contract code can behave differently than people expect because Solidity inserts checks before high-level external calls.

This matters when contracts use try/catch for “safe” integrations with unknown or optional external contracts. The catch block may not be a complete safety net unless the call boundary and ABI assumptions are handled carefully.

Curious if others here have run into try/catch edge cases in audits or production code.

It’s something that often surprises folks. I’ve seen bugs from time to time that deal with it. I don’t remember where but there was a batch caller that was meant to send out rewards and it looped through all the contracts trying to call them to receive the NFT or 1155 token or something. But if one was an EOA the function would revert trying to get to the try/catch and not be caught So you could DOS the contract by putting an EOA in the list of contracts to send rewards to.
There was a lightning talk about it in the last DSS that I like: youtube watch?v=XqzIw_r8JU8