Card 07/ 10
Swallowed Exceptions: The Cost of catch (Exception e) {}
The import job reports success every night, and the table has not grown for three weeks.
An empty catch (Exception e) { } hides every failure in its try block: it catches bugs you never anticipated as well as the failure you meant, and it discards the message and stack trace. Catch the narrowest type you can respond to, and record what happened.
The import job reports success every night. The table it was supposed to fill has had the same row count for three weeks:
for (Path file : files) {
try {
importFile(file);
} catch (Exception e) {
}
}Two separate things are wrong on line 4, and either alone would be enough to lose the failure.
| What it does | Why it costs |
|---|---|
Catches Exception | Takes every runtime bug as well, such as a null dereference, a bad cast (a forced conversion to the wrong type) or an arithmetic error, none of which this handler could have anticipated |
| Does nothing with it | The stack trace, the message and the cause were all captured and are now unreachable |
Catch the narrowest type you can respond to, and if the response is "keep going with the next file", say so and log which file and why.
for (Path file : files) {
try {
importFile(file);
} catch (ImportException e) {
failures.add(file);
log.warn("skipping {}", file, e);
}
}
if (!failures.isEmpty()) { throw new ImportFailed(failures); }An empty catch is occasionally right, such as a close that fails during cleanup, and when it is, a one-line comment saying why is the difference between a decision and an oversight.
← → move through sections and cards