The environment in which our code runs tends to be imperfect: users will provide invalid inputs, external systems will go down, and our code and other code around it will often contain some number of bugs. Given this, errors are inevitable; things can and will go wrong, and as a result we can’t write robust and reliable code without thinking carefully about error cases. When thinking about errors, it’s often useful to distinguish between errors from which a piece of software might want to recover and those from which there is no sensible way to recover. This chapter starts by exploring this distinction before exploring techniques we can use to ensure that errors don’t go unnoticed and that they’re handled appropriately.