40

In this article, it's said that:

If you want to catch “everything” that would normally happen, then use NonFatal:

import scala.util.control.NonFatal

try {
  operation()
} catch {
  case NonFatal(e) => errorHandler(e)
}

But I usually use Exception:

try {
  operation()
} catch {
  case e: Exception => errorHandler(e)
}

I would like to know what is the difference between NonFatal and Exception in Scala? Does Exception in Scala include fatal exception?

AFAIK in java, Exception is for non-fatal error and Error is for fatal error. Is scala different with java in term of Exception?

Which way is the correct one to catch non-fatal exception?

null
  • 8,669
  • 16
  • 68
  • 98

2 Answers2

40

Edit: updated for the latest Scala version (2.11+ has a different definition of NonFatal.apply).


NonFatal is just a convenient extractor which is defined in scala.util.control:

object NonFatal {
   /**
    * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal
    */
   def apply(t: Throwable): Boolean = t match {
     // VirtualMachineError includes OutOfMemoryError and other fatal errors
     case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
     case _ => true
   }
  /**
   * Returns Some(t) if NonFatal(t) == true, otherwise None
   */
  def unapply(t: Throwable): Option[Throwable] = if (apply(t)) Some(t) else None
}

There is no special "fatal" kind of exceptions on JVM - Errors are not always "fatal", they're just a special kind of internal exceptions. "Fatal" exceptions are just a list of exceptions used in NonFatal definition. In this terminology all Exceptions except InterruptedException are considered non-fatal. It makes sense to consider InterruptedException fatal because it means that the thread is interrupted, so if you want to handle it you should do it explicitly.

NonFatal extractor also handles ControlThrowables correctly. These are exceptions which are thrown by special control transfer functions like break inside breakable.

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • 1
    I'm not still really clear, so `NonFatal` and `Exception` is roughly same? – null Apr 20 '15 at 13:20
  • 3
    No. When you match on `e: Exception` you are inspecting the actual type of the exception, much like `catch (Exception e)` in Java. If the exception is a subclass of `Exception`, the exception will be caught. `NonFatal`, on the other hand, is an extractor object which performs additional logic on the matched value. In this case `NonFatal(e)` will match (and catch the exception) only when `NonFatal.apply(e)` returns `true`. When it returns `true` you can see in the piece of code above. – Vladimir Matveev Apr 20 '15 at 13:31
  • You should read some tutorial on extractors, like [this](http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html) one, if you don't know what they are. – Vladimir Matveev Apr 20 '15 at 13:32
  • So `case e: Exception` would also include `VirtualMachineError, ThreadDeath, LinkageError, ControlThrowable, NotImplementedError`? Do you also suggest `case NonFatal(e)` should be used instead of `case e: Exception` in most of case? – null Apr 20 '15 at 13:57
  • No, `case e: Exception` won't include `VirtualMachineError` and such because those are `Error`s, not `Exception`s. And yes, I suggest using `NonFatal` because it is more convenient in most cases (due to proper handling of `InterruptedException` or `ControlThrowable`, for example). – Vladimir Matveev Apr 20 '15 at 14:03
  • What's the situation of `InterruptedException` would occur? For the code inside block of `Future{ .. } `, is it possible for it to throw `InterruptedException`? – null Apr 20 '15 at 16:38
  • Yes, it is pretty much possible but is very unlikely as `InterruptedException` is more likely to be thrown with low-level threading code. You can usually find when an exception is used/throw in [their API docs](https://docs.oracle.com/javase/8/docs/api/java/lang/InterruptedException.html). – Vladimir Matveev Apr 20 '15 at 16:53
  • `ControlThrowable` is a `Throwable`, not an `Exception`, and as such won't be caught by `case e: Exception`; `case NonFatal(e)` won't differ in its handling there. – pdxleif Feb 10 '17 at 20:05
  • 3
    why is StackOverflow NonFatal? – blueFast Jun 15 '17 at 08:41
  • 1
    @dangonfast That was changed in Scala 2.11 (and 2.12); `StackOverflow` is fatal. See [NonFatal docs](https://www.scala-lang.org/api/2.11.6/#scala.util.control.NonFatal$). Also more info in the [issue tracker](https://issues.scala-lang.org/browse/SI-7999) and the [fixing pull request and google groups discussion](https://github.com/scala/scala/pull/3182) – abc123 Jul 13 '18 at 03:48
6

Exceptions don't get mentioned very much in Scala, but they're still what is done multiple times when dealing with unexpected failure.

When we look for When to catch java.lang.Error? multiple answers, and opinions will be present, but let's focus on the common part.

A reasonable application should not try to catch

  • "An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch."
  • "Thrown to indicate that the Java Virtual Machine is broken or has run out of resources necessary for it to continue operating."
  • "Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector."

NonFatal is an Extractor of non-fatal Throwables. Will not match fatal errors like VirtualMachineError (for example, OutOfMemoryError and StackOverflowError, subclasses of VirtualMachineError), ThreadDeath, LinkageError, InterruptedException, ControlThrowable, that are part of the failures a reasonable application should'nt try to catch.

With this in mind, we could write code that catches all harmless Throwables can be caught by:

try {
  // dangerous code
} catch {
  case NonFatal(e) => log.error(e, "Something not that bad.")
}

If we look at the apply method we can see it very clearly.

object NonFatal {
   /**
    * Returns true if the provided `Throwable` is to be considered non-fatal, or false if it is to be considered fatal
    */
   def apply(t: Throwable): Boolean = t match {
     // VirtualMachineError includes OutOfMemoryError and other fatal errors
     case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable => false
     case _ => true
   }
}
OBarros
  • 132
  • 1
  • 9