Well, so there's two theories there... there's the theory you're saying, that the caller might want to know it's a filenotfound, and handle it programmatically... and there's the theory that the fact that you're doing file I/O is an implementation detail that callers shouldn't depend on, because it could change. What side of that argument you fall on depends a lot on the specifics of your code, and how much you want to sanitize your errors (and how likely you think callers will actually be able to do something programmatically about an error).
I agree with both sides, and what I do in any particular situation depends a lot of on the details of what I'm doing.
No, this doesn't fit either case. If you are going to return the error details in a way the human user can read, then you really ought to just return them symbolically, because a programmer will start running regexes over your error codes to figure out what happened. If you're going to not show the underlying error, then either be willing to entirely hide it (sometimes appropriate, probably not for the usual IO case but there are internal errors for which a translation is appropriate) or wrap and pass up, but don't wrap and pass up as a string. That's the worst of all worlds.
I'm pretty much never going to feel bad about breaking someone's code that runs a regex over the error message I return. I can't stop people from writing bad code. Then again, I'm also careful to write my own code such that consumers of it always have a reasonable way to programmatically detect all the errors I know exist, and anything else is just "something really unexpected went wrong, here's a string to log if you want to".
There's always going to be a category of errors that your code just doesn't understand. Giving the caller a string to log with some context is a lot better than returning just a generic error with no context. Returning the specific error is also possible, and sometimes that may be appropriate, but it can cause just as many problems as the other way... but at least the guy writing a regex should know better, and anyone who reads his code is going to notice it and tell him it's very likely to break. However, if someone type-checks the error you're returning, that looks like pretty valid code, so it's less likely to get red-flagged.... until you change your implementation, and that code now breaks.
I really don't think we disagree terribly much on this btw. Like I said, it depends a lot on the situation. Defining policies for errors is hard. There's always tradeoffs, like anything else.
I agree with both sides, and what I do in any particular situation depends a lot of on the details of what I'm doing.