Yes of course. That's why I mentioned polymorphism. A FileNotFoundException and NoDiskSpaceException can inherit from IOException for example. With polymorphic error classes, a caller can decide if they want to handle the different cases individually, or just catch the overarching IOException.
All this flexibility comes for free when your use your language's type system, whereas with plain error codes you would have to implement grouping yourself manually with some kind of lookup table.
This classical, rigid OO way of thinking assumes there is single inheritance chain but that's not the case more often than not. For example i/o can have hierarchy based on operating system, kind of i/o (network, filesystem etc), access type (read/write), nature (idempotent etc), severity, abstraction (hardware, os, library, app levels), source (calee/caller errors or input/configuration/external service errors) etc.
Ok, then use traits or composition instead of inheritance. Still using the type system, still better than hardcoding a complicated error code mapping system. I used inheritance as an example, my main point is to use the type system when dealing with... types of things.