Return codes have problems. Callers can ignore them, leading to silent failures. Every function must check every call's return value, cluttering code with error handling. Return values mix error status with actual results.
A function returning -1 for error and positive numbers for success can't return -1 as a valid result. Exceptions separate error handling from normal logic. You write the happy path then handle errors in a dedicated catch block.
Failures can't be silently ignored.