Understanding Ruby's strange "Errno" exceptions

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::* . This post discusses what these exceptions are and how to interpret them.

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::*.

Exception
  StandardError
    ...
    SystemCallError
      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
=> [:NOERROR, :EPERM, :ENOENT, :ESRCH, :EINTR, :EIO, :ENXIO, :E2BIG, :ENOEXEC, :EBADF, :ECHILD, :EAGAIN, :ENOMEM, :EACCES, :EFAULT, :ENOTBLK, :EBUSY, :EEXIST, :EXDEV, :ENODEV, :ENOTDIR, :EISDIR, :EINVAL, :ENFILE, :EMFILE, :ENOTTY, :ETXTBSY, :EFBIG, :ENOSPC, :ESPIPE, :EROFS, :EMLINK, :EPIPE, :EDOM, :ERANGE, :EDEADLK, :ENAMETOOLONG, :ENOLCK, :ENOSYS, :ENOTEMPTY, :ELOOP, :EWOULDBLOCK, :ENOMSG, :EIDRM, :ECHRNG, :EL2NSYNC, :EL3HLT, :EL3RST, :ELNRNG, :EUNATCH, :ENOCSI, :EL2HLT, :EBADE, :EBADR, :EXFULL, :ENOANO, :EBADRQC, :EBADSLT, :EDEADLOCK, :EBFONT, :ENOSTR, :ENODATA, :ETIME, :ENOSR, :ENONET, :ENOPKG, :EREMOTE, :ENOLINK, :EADV, :ESRMNT, :ECOMM, :EPROTO, :EMULTIHOP, :EDOTDOT, :EBADMSG, :EOVERFLOW, :ENOTUNIQ, :EBADFD, :EREMCHG, :ELIBACC, :ELIBBAD, :ELIBSCN, :ELIBMAX, :ELIBEXEC, :EILSEQ, :ERESTART, :ESTRPIPE, :EUSERS, :ENOTSOCK, :EDESTADDRREQ, :EMSGSIZE, :EPROTOTYPE, :ENOPROTOOPT, :EPROTONOSUPPORT, :ESOCKTNOSUPPORT, :EOPNOTSUPP, :EPFNOSUPPORT, :EAFNOSUPPORT, :EADDRINUSE, :EADDRNOTAVAIL, :ENETDOWN, :ENETUNREACH, :ENETRESET, :ECONNABORTED, :ECONNRESET, :ENOBUFS, :EISCONN, :ENOTCONN, :ESHUTDOWN, :ETOOMANYREFS, :ETIMEDOUT, :ECONNREFUSED, :EHOSTDOWN, :EHOSTUNREACH, :EALREADY, :EINPROGRESS, :ESTALE, :EUCLEAN, :ENOTNAM, :ENAVAIL, :EISNAM, :EREMOTEIO, :EDQUOT, :ECANCELED, :EKEYEXPIRED, :EKEYREJECTED, :EKEYREVOKED, :EMEDIUMTYPE, :ENOKEY, :ENOMEDIUM, :ENOTRECOVERABLE, :EOWNERDEAD, :ERFKILL, :EAUTH, :EBADRPC, :EDOOFUS, :EFTYPE, :ENEEDAUTH, :ENOATTR, :ENOTSUP, :EPROCLIM, :EPROCUNAVAIL, :EPROGMISMATCH, :EPROGUNAVAIL, :ERPCMISMATCH, :EIPSEC]

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.

What to do next:
  1. Try Honeybadger for FREE
    Honeybadger helps you find and fix errors before your users can even report them. Get set up in minutes and check monitoring off your to-do list.
    Start free trial
    Easy 5-minute setup — No credit card required
  2. Get the Honeybadger newsletter
    Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo

    Starr Horne

    Starr Horne is a Rubyist and Chief JavaScripter at Honeybadger.io. When she's not neck-deep in other people's bugs, she enjoys making furniture with traditional hand-tools, reading history and brewing beer in her garage in Seattle.

    More articles by Starr Horne
    Stop wasting time manually checking logs for errors!

    Try the only application health monitoring tool that allows you to track application errors, uptime, and cron jobs in one simple platform.

    • Know when critical errors occur, and which customers are affected.
    • Respond instantly when your systems go down.
    • Improve the health of your systems over time.
    • Fix problems before your customers can report them!

    As developers ourselves, we hated wasting time tracking down errors—so we built the system we always wanted.

    Honeybadger tracks everything you need and nothing you don't, creating one simple solution to keep your application running and error free so you can do what you do best—release new code. Try it free and see for yourself.

    Start free trial
    Simple 5-minute setup — No credit card required

    Learn more

    "We've looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release."
    — Michael Smith, Cofounder & CTO of YvesBlue

    Honeybadger is trusted by top companies like:

    “Everyone is in love with Honeybadger ... the UI is spot on.”
    Molly Struve, Sr. Site Reliability Engineer, Netflix
    Start free trial
    Are you using Sentry, Rollbar, Bugsnag, or Airbrake for your monitoring? Honeybadger includes error tracking with a whole suite of amazing monitoring tools — all for probably less than you're paying now. Discover why so many companies are switching to Honeybadger here.
    Start free trial