Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unified I/O error type #47771

Open
maleadt opened this issue Dec 1, 2022 · 3 comments
Open

Unified I/O error type #47771

maleadt opened this issue Dec 1, 2022 · 3 comments
Labels
design Design of APIs or of the language itself error handling Handling of exceptions by Julia or the user I/O Involving the I/O subsystem: libuv, read, write, etc.

Comments

@maleadt
Copy link
Member

maleadt commented Dec 1, 2022

Julia currently throws different kinds of IO exceptions:

julia> readdir("/foo")
ERROR: IOError: readdir("/foo"): no such file or directory (ENOENT)
julia> read("/foo")
ERROR: SystemError: opening file "/foo": No such file or directory

IOError happens when a libuv wrapper throws, and the contained error code is an libuv error code (e.g. Base.UV_ENOENT) while SystemError happens when a libc call fails and the code is a libc errno (e.g. Libc.ENOENT). The fact that this difference is exposed to the user, feels like an implementation detail leaking into the upper layers of abstraction.

Normally this doesn't matter much, because one is assumed to e.g. check if the path exists before performing the I/O operation. The application I'm currently dealing with however, is really racey, and to avoid TOCTOU bugs I just perform the operation and deal with the error after the fact. That works fine, except for the annoyance of having to deal with two highly similar error types. Because it's not really documented when libuv or libc are used, I end up with code like:

try
    ...
catch err
    if (isa(err, SystemError)  && err.errnum == Libc.ENOENT) ||
       (isa(err, Base.IOError) && err.code == Base.UV_ENOENT)
        ...
    else
        ...
    end
end

I haven't really put in much thought on how to solve this; probably a unified error type that isn't tied to libc/libuv?

@maleadt maleadt added I/O Involving the I/O subsystem: libuv, read, write, etc. error handling Handling of exceptions by Julia or the user design Design of APIs or of the language itself labels Dec 1, 2022
@vtjnash
Copy link
Sponsor Member

vtjnash commented Dec 1, 2022

libuv has an API for that (https://github.com/libuv/libuv/blob/v1.x/include/uv.h#L390), though it may be somewhat lossy (does not convert 100% of system codes, and combines many of them on Windows)

@maleadt
Copy link
Member Author

maleadt commented Dec 1, 2022

libuv has an API for that (https://github.com/libuv/libuv/blob/v1.x/include/uv.h#L390), though it may be somewhat lossy (does not convert 100% of system codes, and combines many of them on Windows)

Oh interesting. IOError could maybe gain an original_code field to deal with the lossy conversion.
Would this be considered a breaking change? I see surprisingly few packages checking against SystemError.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Dec 1, 2022

Yes, that would be useful, and probably would let us merge these. FWIW, uv_fs_get_system_error can be used to recover the original_code in some cases now. See discussion in libuv/libuv#2348 (plus, we could make ABI changes in libuv v2 to get more of these also, and also would more fully combine SystemError, WindowsErrorInfo, and IOError)

Also FWIW, the SystemError type already has extrainfo to handle doing this in the other direction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
design Design of APIs or of the language itself error handling Handling of exceptions by Julia or the user I/O Involving the I/O subsystem: libuv, read, write, etc.
Projects
None yet
Development

No branches or pull requests

2 participants