Airbrake Blog

What is a Runtime Error?

Written by Frances Banks | Oct 11, 2017 12:00:59 AM

A runtime error is an application error that occurs during program execution. Runtime errors are usually a category of exception that encompasses a variety of more specific error types such as logic errors, IO errors, encoding errors, undefined object errors, division by zero errors, and many more. In this article we'll examine exactly what a runtime error is and how it compares to other exception categories. We'll also look at some basic code samples that will illustrate how to fix runtime errors, so let's get started!

Interpretation vs Compilation

Most programming languages fall into one of two categories, both of which describe how source code is executed by the underlying machine:

  • Interpretation - Interpreted programming languages are directly executed by an interpreter, without altering or transforming the source code prior to execution. This process reads each statement one at a time, translating each into a sequence of instructions that have already been converted into machine code for rapid execution. A few of the well-known interpreted languages are PHP, JavaScript, and Perl.
  • Compilation - Upon executing the application, compiled programming languages first pass application source code through a compiler, which transforms the source code into a more efficient form of machine code. This compiled machine code is often referred to as bytecode. The machine code is then passed along to an interpreter, which executes each instruction one by one, just as with an explicitly interpreted language. Some popular compiled languages are C and its many derivatives, BASIC, Haskell, and Ruby.

That said, it's important not to get too hung up on the distinction between interpretation and compilation. Technically, a programming language itself is neither compiled nor interpreted. Instead, the difference is simply based on how the language is implemented. Many programming languages, including many of the most popular used today, have the means to be implemented by using both interpreters and compilers, and some of the most reliable languages heavily rely on a combination of the two techniques.

Runtime Errors vs Compile Time Errors

Since runtime errors occur during execution of the application, we can deduce that such errors occur during the interpretation phase. On the other hand, compile time errors occur during the compilation phase. Compilation errors include issues that can be picked up by the compiler, such as improper syntax, invalid references, and undeclared variables.

To break it down a bit further, let's consider a specific type of compilation error, the syntax error. A syntax error occurs when the compiler or interpreter can't determine the intention of your code. For example, let's consider the following simple C# code:

int Sum(int a, int b)
{
return a + b;
}

Here we have a method (also known as a function or subroutine in other languages) called Sum that expects two numeric arguments. It adds a and b together and returns the result. Passing in 5 and 3 to the Sum(int a, int b) method results in a returned value of 8, as expected:

Console.WriteLine(Helper.Sum(5, 3)); // 8

However, watch what happens when we remove the single semicolon (;) from the code, like so:

int Sum(int a, int b)
{
return a + b
}

Trying to execute this code first runs it through the C# compiler, which attempts to transform it into bytecode. However, the compiler immediately spits out a compiler error with the error code of CS1002:

; expected in Helper.cs:line 34

The compiler detected a missing semicolon. A semicolon in required at the end of every statement in C#. A statement may span more than one line.

The compiler cannot determine the intention of our code because its attempt to parse the code results in an abnormal pattern. This parsing process is known as lexical analysis (or tokenization), which is the act of converting characters within a line of source code into a sequence of tokens, which are simply pre-defined instructions known to the interpreter. By breaking all code into such tokens, a compiler is able to determine what your original source code is intended to do, and generate the machine code capable of achieving that goal.

However, if a compiler has trouble with this tokenization process a compilation error occurs. In the C# example above, the compiler expects only a handful of potential tokens (and, thus, character series) to follow the return a + b statement. One such token is the semicolon that indicates the end of a statement, but it does not expect the closing brace (}) that it actually encountered above, so the compilation error CS1002 was thrown.

On the other hand, a runtime error occurs when the runtime understands the intention of the code, but fails to execute all coded instructions as currently written. Since this type of error comes up during execution -- after compilation has taken place (i.e. during interpretation) -- runtime errors must typically be caught and handled directly within the source code. Such errors are commonly referred to as bugs during application development, since these sorts of exceptions will force the majority of the debugging that takes place.

As a simple example, here we have the Divide() method that, like Sum(), does just what the name implies:

double Divide(int a, int b)
{
return a / b;
}

There are no syntax errors, so compilation occurs without a hitch. However, let's see what happens if we pass in the values 5 and 0 to the Divide() method:

Console.WriteLine(Helper.Divide(5, 0));
// System.DivideByZeroException: 'Attempted to divide by zero.'

During execution a System.DivideByZeroException is thrown, indicating that we've attempted to divide by zero. This is an example of a common runtime error that must be planned for, since it's difficult to know whether some calculation within a larger application might attempt to divide a value by zero at some point.

Every language is difference, but in the case of C# we can surround the problematic statement with a try-catch block, which allows us to gracefully handle a DivideByZeroException:

double? Divide(int a, int b)
{
try
{
return a / b;
}
catch (DivideByZeroException e)
{
// Output any DivideByZeroException, then continue.
Console.WriteLine(e);
}
return null;
}

Now, any attempt to divide by zero will throw a DivideByZeroException, which is caught and output the console before returning a null (empty) value. In short, such exception handling is the primary way to handle runtime errors.

That said, even the best laid plans of mice and men often go awry. While production defects are far from inevitable, it's critical to have a safety net in the unlikely occurrence that something unexpected happens in your application after it's already out there. This is where the power of error monitoring software comes into play. Even during development, but particularly after production release, error monitoring software provides that life line your organization needs to ensure your software remains fully functional. Any unforeseen defects are immediately identified and reported to your team, without the need for user-generated feedback or awkward error reports. Check out Airbrake's exception handling tools today to see how you or your team can keep on top of any defects that slipped through the cracks during production.