Concepts / Handling Errors Gracefully in Programs

Handling Errors Gracefully in Programs

A program is correct when it accomplishes the task it was designed to perform, and this is judged against the inputs it is meant to handle.

  • Programming

Why a Crash Is Not Automatically a Bug

When a program crashes, it is tempting to conclude that the program is wrong. The reasoning seems simple: if the program stops with an error, it must have failed, and if it stays calm and produces a neat result every time, it must be doing its job. This instinct feels reasonable, but it merges two separate questions that programmers need to keep apart. The first question is whether a program does its job. The second is whether it survives situations it was not meant to face. A program can answer yes to the first and no to the second, and the difference matters when you decide how to build and judge software.

The two ideas that this article separates are called correctness and robustness. Both are ways of evaluating a program, but they measure different things. The sections that follow start with a concrete program, make you predict how it should be judged, and then build the definitions from that example.

Predicting How a Sorting Program Should Be Judged

Imagine a program whose job is to read a list of numbers from the user and print them in sorted order. Suppose that for every valid list of numbers it sorts correctly. But when the user types a word such as seven instead of a number, the program stops with an error and quits. Before reading on, decide on two answers: would you call this program correct, and would you call it robust?

What do you think happens?

Is the sorting program correct, and is it robust?

  • Correct and robust

  • Correct but not robust

  • Robust but not correct

  • Neither correct nor robust

Reveal answer
Answer:

Correct but not robust

The program does its designed task, sorting numbers, for every valid list it is given, so it is correct. Crashing on the word seven is a failure to handle illegal input gracefully, which is a robustness failure. The crash tells you about robustness and says little by itself about whether the program does its job.

What Correctness Means

Definition

A program is correct when it accomplishes the task it was designed to perform, and this is judged against the inputs it is meant to handle.

The key phrase is judged against the inputs it is meant to handle. Correctness is not measured against every imaginable input. It is measured against the inputs the program is supposed to accept. For the sorting program, the designed task is to read numbers and print them in sorted order. For that program to be correct, it has to produce sorted output for any set of input numbers. A single set of numbers that comes out unsorted is enough to show that the program fails its task.

Tracing the Sorting Program on Valid Input

Worked example

Checking whether the sorting program is correct

The program reads a list of numbers from the user and prints them in sorted order. Is it correct for the input 4, 1, 9?

  1. 1
    Identify the designed task

    The task is to read numbers and print them in sorted order.

  2. 2
    Identify the inputs the program is meant to handle

    The inputs are lists of numbers, so 4, 1, 9 is a valid input in the expected range.

  3. 3
    Compare the output with the task

    The sorted output is 1, 4, 9. This matches the designed task.

  4. 4
    Decide on correctness for this input

    This input is handled correctly. To show the program is correct in general, the same check must succeed for any set of valid numbers. One unsorted output would be enough to show it is not correct.

Result

For valid input, the output matches the designed task, so the program is behaving correctly on this case.

What Robustness Means

Definition

A program is robust when it handles illegal inputs and unexpected situations in a reasonable way, such as reporting an error and ignoring bad input rather than crashing.

Robustness is judged against different conditions than correctness. Correctness is tested with the inputs the program is supposed to handle, and asks whether it reaches its goal. Robustness is tested with inputs outside that expected range, and asks whether the program fails gracefully. Because the conditions differ, a program can pass one test and fail the other.

The standard for robustness is reasonableness, not perfection. The sorting program does not need to guess what the user meant by the word seven, and it does not need to recover every possible value. It needs to avoid a crash or nonsensical output, and to respond in a way a sensible person would consider acceptable. The source contrasts a robust response, which prints an error message and ignores the bad input, with a non-robust program that might crash or give nonsensical output when it meets the same bad input.

How a Bad Input Travels Through a Program

The flowchart below follows a single item of input through the sorting program. The key question is what happens at the decision point: when the input is illegal, does the program reject it, recover, or crash? A robust program sends illegal input down a path that reports the problem and continues. A non-robust program has no such path, so the bad input falls through to a failure.

Read next itemIs it a number?Add to listPrint sorted listPrint error messageIgnore bad inputStop with erroryesno, robustall items readno, non-robust
When the input is illegal, where does control go next, and does the program reject it, recover, or crash?

The flowchart has two branches for illegal input. The robust branch reports the error and ignores the bad input, so the program keeps going and can still produce a sorted result from the valid items. The non-robust branch ends in a crash. Both programs can be correct on valid lists; the only difference is what they do on the illegal path.

python
Output (expected)
Error: ignored non-numeric input: seven
[1, 4]
Notes on the code
  • int(item) raises ValueError when the item is not a number such as seven
  • The except branch is the robust path: it reports the error and skips the bad item
  • The valid items are still sorted, so the program continues to do its task

Graceful Handling Between User and Program

Graceful handling is easiest to see as a sequence of exchanges. The user supplies input, the program detects that the input is illegal, an error handling step produces a message, and the program continues rather than stopping. The diagram shows the order in which these three parties exchange input, failure, and message.

UserSorting programError handlertypes sevennon-numeric valueignore this itemerror message, then sorted result
In what order do the user, the program, and the error handler exchange input, failure, and message?

The error message is part of the robust response. The source describes a program that prints an error message and ignores the bad input. The message tells the user what went wrong, and the program does not depend on guessing what the user meant. The purpose is a reasonable reaction, not a perfect interpretation of every mistake.

Correctness Versus Robustness Side by Side

The two properties are easiest to tell apart by comparing two programs, each of which has one property without the other. The first program is correct but not robust, like the sorting program that crashes on seven. The second is robust but not correct: it prints a friendly error message for every input, including valid lists, and never sorts anything. Its handling of bad input looks polite, but it fails its designed task.

PropertyMeasured againstValid inputIllegal inputQuestion it answers
CorrectnessThe inputs the program is meant to handleProduces the required resultNot the main testDoes the program do its job?
RobustnessInputs outside the expected rangeNot the main testReports the error and continuesDoes the program fail gracefully?
Correct but not robustValid lists sorted properlySorted outputCrashesDoes its job, but fails on bad input
Robust but not correctError messages for bad inputWrong or missing resultReports the error and continuesHandles bad input politely but fails its task

Correctness and robustness are judged against different conditions, so a program can have one property without the other.

Sorted outputvalid listWrong resultvalid listCrashword sevenError messageword seven
How do a program that is correct but not robust and a program that is robust but not correct differ on valid and invalid inputs?

Deciding How Much Robustness Is Enough

The source makes a clear distinction here. Every program should be correct. Not every program needs to be completely robust. Whether a program needs more robustness depends on who will use it and how it will be used. Robustness is a design decision about how much care the situation deserves, not a checkbox every program must tick.

Mistakes About Crashes and Correctness

  • Common mistake

    Treating any crash as proof that the program is incorrect

    What it looks like

    The sorting program stops with an error when the user types seven, and someone concludes it does not sort correctly.

    Why it fails

    A crash on illegal input is evidence about robustness. The program may still sort every valid list correctly.

    Do this instead

    Ask separately whether the program does its designed task on valid inputs, and whether it handles illegal inputs reasonably.

  • Common mistake

    Treating a calm, neat output as proof that the program is correct

    What it looks like

    A program prints a polite error message for every input, including valid lists, and is judged correct because it never crashes.

    Why it fails

    Correctness is about accomplishing the designed task. A program that never sorts anything is not correct, even if it is polite.

    Do this instead

    Check the output for valid inputs against the designed task before judging correctness.

  • Common mistake

    Demanding full robustness from every program

    Why it fails

    The source states that not every program needs to be completely robust, and the required level depends on who uses it and how.

  • Common mistake

    Assuming a robust program must recover every possible value rather than respond reasonably

Testing Your Judgment on a New Program

MEDIUM

A program is designed to read a temperature in degrees Celsius and print the matching Fahrenheit value. For valid numbers it prints correct conversions. When the user types hot, it prints an error message and asks again. Decide whether the program is correct, whether it is robust, and how much robustness it needs if it is only used by you on your own computer. Explain each answer using the definitions in this article.

Hints
  • Start by naming the designed task and the inputs the program is meant to handle.
  • Judge correctness with valid numbers, and judge robustness with the word hot.
  • Ask who will use the program and what happens if it fails for that user.

Key Takeaways

  • A program is correct when it accomplishes the task it was designed to perform, judged against the inputs it is meant to handle.
  • A program is robust when it handles illegal or unexpected input reasonably, such as reporting an error and ignoring bad input rather than crashing.
  • Correctness and robustness are separate properties, so a program can be correct without being robust, and a crash on bad input says little by itself about whether the program does its job.
  • Every program should be correct, but the required level of robustness depends on who will use the program and how.