Exceptions are easier to use than error codes, because they can be thrown by an deeply nested subroutine and intercepted only at the level where it can be handled.
Error codes need to be passed up the chain so every function that calls another has to pass the error code back to its callers.
An error code doesn't have any clear functional advantages over exceptions since an exception can bundle an error code within it. There may have been a time when error codes might be more efficient than exceptions, but I think the cost of the extra code and the difficulty in maintaining it outweighs any possible advantage there.
However, error codes exist in many applications because they are written in, or were ported from, a language that didn't have exceptions, so it makes sense to keep using a uniform approach to error handling.