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.