If you've ever taken a look at Ruby's exception hierarchy, you may have noticed something weird. In addition to all of the normal exceptions like RuntimeError and NoMethodError, there's an odd reference to Errno::*.


If you've ever had the bad luck to write to disk when the disk is full, or to try to make an API call over a failing network then you've probably seen this type of error in action. You can trigger one right now by attempting to open a file that doesn't exist.

irb> File.open("badfilename.txt")
Errno::ENOENT: No such file or directory @ rb_sysopen - badfilename.txt
    from (irb):9:in `initialize'
    from (irb):9:in `open'
    from (irb):9
    from /Users/snhorne/.rbenv/versions/2.1.0/bin/irb:11:in `<main>'

But what exactly are the Errono exceptions? And why are they treated differently thank other kinds of exceptions?

Adapting Ruby to the OS

The Errno exceptions are essentially an adapter. They connect operating system errors to Ruby's exception system. The operating system handles errors in a different way than Ruby does, so you have to have some kind of adapter.

In ruby, errors tend to be reported as exceptions. But operating system errors are usually just integers. So ruby defines one exception class for each possible OS error. It then sticks all of these exceptions into a module called Errno.

We can use IRB to see all of the exceptions in this module. And boy, are there a lot!

irb> Errno.constants

But why are they named so cryptically? I mean, how am I supposed to ever guess that ENOINT means "File not found?"

...There's actually a very simple answer.

Copied wholesale from libc

Whoever first built the Errno module  just copied the error names directly from libc. So ENOINT, in C, is the name of a macro which contains the integer error code that the OS returns when it can't find a file.

So, to really find out what each of these does the trick is to look at the documentation for the C standard library. You can find a big list of them here. I've excerpted a few of the more relevant ones below:

EPERM Operation not permitted; you can't access the file unless you have permission.
ENOENT File or directory not found.
EIO Input/output error; usually used for physical read or write errors.
EBADF Bad file descriptor. You would get this error if you tried to write to a file you opened only for reading, for example.
ECHILD You tried to manipulate a child process, but there aren't any child processes.
ENOMEM You're out of RAM, and can't allocate any more virtual memory.
EACCES Permission denied; the file permissions do not allow the attempted operation.
ENOTBLK You tried to mount an ordinary file as a device, like a HDD.
EBUSY Resource busy; a system resource that can’t be shared is already in use. For example, if you try to delete a file that is the root of a currently mounted filesystem, you get this error.
EEXIST File exists; an existing file was specified in a context where it only makes sense to specify a new file.
ENOTDIR A file that isn’t a directory was specified when a directory is required.
EISDIR File is a directory; you cannot open a directory for writing, or create or remove hard links to it.
EINVAL Invalid argument. This is used to indicate various kinds of problems with passing the wrong argument to a library function.
EMFILE The current process has too many files open and can’t open any more. Duplicate descriptors do count toward this limit.
EFBIG File too big; the size of a file would be larger than allowed by the system.
ENOSPC No space left on device; write operation on a file failed because the disk is full.
ESPIPE Invalid seek operation (such as on a pipe).
EROFS An attempt was made to modify something on a read-only file system.
EPIPE Broken pipe; there is no process reading from the other end of a pipe
ENOTSOCK A file that isn’t a socket was specified when a socket is required.
ENETUNREACH A socket operation failed because the subnet containing the remote host was unreachable.
ENETRESET A network connection was reset because the remote host crashed.
ECONNABORTED A network connection was aborted locally.
ECONNRESET A network connection was closed for reasons outside the control of the local host, such as by the remote machine rebooting or an unrecoverable protocol violation.
ENOBUFS The kernel’s buffers for I/O operations are all in use. In GNU, this error is always synonymous with ENOMEM; you may get one or the other from network operations.
EISCONN You tried to connect a socket that is already connected. See Connecting.
ENOTCONN The socket is not connected to anything. You get this error when you try to transmit data over a socket, without first specifying a destination for the data.
EDESTADDRREQ No default destination address was set for the socket. You get this error when you try to transmit data over a connectionless socket, without first specifying a destination for the data with connect.
ESHUTDOWN The socket has already been shut down.
ETIMEDOUT A socket operation with a specified timeout received no response during the timeout period.
ECONNREFUSED A remote host refused to allow the network connection (typically because it is not running the requested service).
EHOSTDOWN The remote host for a requested network connection is down.
EHOSTUNREACH The remote host for a requested network connection is not reachable.
ENOTEMPTY Directory not empty, where an empty directory was expected. Typically, this error occurs when you are trying to delete a directory.
EPROCLIM This means that the per-user limit on new process would be exceeded by an attempted fork. See Limits on Resources, for details on the RLIMIT_NPROC limit.