Julia: Exposing UVError doesn't seem right

Created on 5 Aug 2014  Â·  14Comments  Â·  Source: JuliaLang/julia

julia> try
         run(`false`)
       catch err
         dump(err)
       end
ErrorException
  msg: ASCIIString "failed process: Process(`false`, ProcessExited(1)) [1]"

julia> try
         run(`foobar`)
       catch err
         dump(err)
       end
UVError
  prefix: ASCIIString "could not spawn `foobar`"
  code: Int32 -2

I feel like UVError isn't something that should be escaping the Julia internals. For one thing, I doubt anyone not involved with Julia development knows or wants to know what the UV refers to. Having different exception types thrown from the same method (from the users perspective) feels odd to me, although I think thats a much more debatable point.

It bit me recently (I had code like println(fp, err.msg), which threw an error in the error handler...) and I just saw https://github.com/JuliaLang/julia/pull/7822 which made me think of it again.

O error handling help wanted

Most helpful comment

I think this is still relevant (the original example still works). UVError is not exported from Base, so is it correct that its OK to put this off past 1.0?

All 14 comments

Wholeheartedly agree.

We had a long talk about this today. I think our general consensus was that we definitely want granularity of errors, (e.g. not just reporting everything as a generic ErrorException even with a helpful string) correctness of naming (calling things UVError is a little too implementation-specific, but then again most of our I/O subsystem is based on libuv) so as not to confused users, and consistency across Base.

The best short-term proposal (e.g. something within scope for 0.4) I can come up with is the following: Create categories of errors that are small enough to be meaningful (e.g. IOError), and present those to the user with libuv error codes embedded, and friendly string messages given at the time of raising the exception. This has the following benefits:

  • It allows us to present something much more unified and easy-to-understand to the user. We can have exceptions that are specific to IO, and exceptions that are specific to whatever else we use libuv for, and not mix them up.
  • Users who want to catch exceptions that map to a very specific situation can check the libuv error code. Users who want to do that kind of thing are necessarily advanced enough to understand that there are such a thing as error codes, and making those available and easily digested is worthwhile.
  • If an error code maps to an automatic message that doesn't mean much to the user at first glance, (such as this one) can be overridden to something much more user-friendly. Instead of getting "Error: Connection aborted" you can get "Error: Server socket was closed while attempting to accept a client"

I've been complaining about UVError for some time:

https://github.com/JuliaLang/julia/issues/6825#issuecomment-42912316
https://github.com/JuliaLang/julia/issues/1924#issuecomment-14276785

See also #4744

Also, an exception type should describe a problem, not which library threw it.

15879

I think this is still relevant (the original example still works). UVError is not exported from Base, so is it correct that its OK to put this off past 1.0?

Agree this should be replaced with SystemError, IOError, etc.

Shouldn't this be on the 0.7 milestone rather than 1.0?

Yes I think this could be on the 0.7 milestone, but it's fairly minor since code probably doesn't reference UVError too often.

In #15879 a subset of UVError was replaced by DNSError. A quick scan of the possible UV_ error codes suggests that there is scope to continue this pattern and split out more specific error types in future (IOPermissionError?, IOBusyError?, HostUnreacableError?).

However, I'm concerned that baking IOError into 1.0 with its current meaning would preclude splitting out more fine-grained error types.

e.g if the code below was written for Julia 1.0, it would stop working if IOPermissionError was later added as a fine-grained error type for UV_EACCES.

catch e
    if e isa IOError && e.code == UV_EACCES
        bar()
    else
        rethrow(e)
    end
end

It might be better for IOError to be an abstract type so that a future IOPermissionError could be <: IOError. Then code like the following would continue to work as fine-grained error types are added:

catch e
    if e isa AbstractIOError && errorcode(e) == UV_EACCES
        bar()
    else
        rethrow(e)
    end
end

Ok so should we just add AbstractIOError above IOError (and EOFError and DNSError)?

Technically making IOError abstract in the future is not a breaking change. Dispatch on it and fields that use it as a type would continue to work as expected. Only code that was written to the new subtypes would see any difference and that's not breaking.

Then perhaps the IOError.code field should be renamed to uvcode (syscode? internalcode?). Otherwise future IOError subtypes can't use the field name code.
e.g. if HTTPError <: IOError then .code would be 404 not a UV_ code.

The code field is implementation-defined anyway; it's pretty hard to commit to a meaning for all of its possible values. HTTPError would specify that .code is an HTTP error code, but IOError says the code is implementation-defined and you don't really know what it is.

Was this page helpful?
0 / 5 - 0 ratings