What matters is whether the component is _fallible_ or not. If a component can fail, its caller has to be able to deal with the failure, even if it deals with the failure by also failing (propagating the failure).
Your example already could fail (on a missing file today, on a network problem tomorrow), so a well-designed API contract (which doesn't unnecessarily expose the _cause_ of the failure) wouldn't have to change. The implementation detail is the cause of the failure, not the fact that it can fail.
All components can fail; even if just by programmer error.
There is a difference between expected failures and unexpected failures. I like to use the example of int.Parse() and int.TryParse() in C#. These functions do exactly the same thing but treat errors very differently. You use int.Parse() when you expect the parse to always succeed. It will raise an exception if the parse fails. int.TryParse() doesn't raise an exception, it returns a boolean and you use it when you expect the parse to fail.
Returning a boolean indicating success/failure can be part of the API contract with well defined semantics. But that can't help you with unexpected errors that are part of the implementation.
For unexpected failures, you want to know the cause of it and you want as much implementation detail as possible.
I can't recover from a missing file, but I can recover from temporary network problems. If the file is missing, I want to know what file is missing so I can fix that manually.
Your example already could fail (on a missing file today, on a network problem tomorrow), so a well-designed API contract (which doesn't unnecessarily expose the _cause_ of the failure) wouldn't have to change. The implementation detail is the cause of the failure, not the fact that it can fail.