Call for volunteers: a CLC proposal to improve the printing of IO errors

On the HF Slack instance, a user dropped by with a request: can programs that fail due to missing files be better about reporting to users which file was missing? The user in question is providing feedback about how many programs in Haskell can be improved in one fell swoop, but they’re not really in a position to fix it themselves.

Concretely, they’re using dhall-to-json, which displays the following when it tries to open a broken symlink:

: openFile: does not exist (No such file or directory)

: openFile: does not exist (No such file or directory)

: openFile: does not exist (No such file or directory)

In other words, some file or another doesn’t exist, but the user isn’t told which one. While following symlinks to make the error message would be error prone, the default way of converting an IOError into a string could at least describe the filename that openFile was called with.

@davean dug out the responsible code from base:

data IOException
 = IOError {
     ioe_handle   :: Maybe Handle,   -- the handle used by the action flagging
                                     -- the error.
     ioe_type     :: IOErrorType,    -- what it was.
     ioe_location :: String,         -- location.
     ioe_description :: String,      -- error type specific information.
     ioe_errno    :: Maybe CInt,     -- errno leading to this error, if any.
     ioe_filename :: Maybe FilePath  -- filename the error is related to.
   }

The IOError itself has enough information, and each program could in principle write an error handler that works. In POSIX:

openFile :: FilePath -> IOMode -> IO Handle
openFile fp im = 
  catchException
    (openFile' fp im dEFAULT_OPEN_IN_BINARY_MODE True)
    (\e -> ioError (addFilePathToIOError "openFile" fp e))

But in practice, many programs will use the default handler, so improving it is important.

Making the string output for this exception is likely to cause difficulties for some test suites out there, so it should be a CLC proposal. A small one, though!

If you’ve been wanting to get more deeply involved in Haskell development, and you’d like to have a small positive effect on a whole ton of lives, this might be the CLC proposal for you to write and implement - the actual change is likely to be a line or two.

12 Likes

Yes please! This is such a sad issue and it affects essentially all CLI programs written in Haskell.

5 Likes

I just renamed the post - hopefully this new title will attract more attention.

2 Likes

I don’t understand what is being suggested here. instance Exception IOException does display ioe_filename if one was set. The problem is that some functions don’t set it, for various reasons.

E. g., mkFD throws dreaded “openFile: file is locked” exceptions without telling us which file it is. But it cannot set ioe_filename simply because at this level there are no filenames, just raw file descriptors. A caller of mkFD should catch the exception and set ioe_filename if it knows it.

1 Like

Isn’t that the solution David was suggesting here?

i.e. We should change System.IO.openFile to be implemented as this?

It’s already implemented this way: openFile.

We could audit base, unix, Win32, directory and maybe process to look for such issues.