# The signature carries the failure

In `file-system`, `read` fails two ways that have nothing in common. The path might be
`"/notes//todo.txt"`, which no tree could ever satisfy. Or `/notes` might not exist yet. C++ gives
you one category for both.

The obvious Java move: stop anyone ignoring the second. Declare
`NoSuchPathException extends Exception` and it becomes *checked*: the signature reads
`String read(String path) throws NoSuchPathException`, and so does every caller up the chain.
Miss one and the build stops:

```
error: unreported exception NoSuchPathException; must be caught or declared to be thrown
```

That is a contract enforced, not a bug caught. A checked exception is a second return type,
and every caller states its answer. `noexcept` never asked that.

Then the fourth failure type arrives. Four exceptions over eight methods puts a `throws` clause on
nearly every signature, and pushes callers into `catch (Exception e) {}`.

So `file-system` sorts on a different axis first: whose mistake was it. A malformed path is
`IllegalArgumentException`, unchecked and outside the hierarchy, because no `catch` would repair
it. The four domain outcomes share an abstract `FileSystemException` root, so a caller
branches on which one happened.

All of them are unchecked. The rule that decided it: unchecked for what the caller should have prevented,
checked for what a reasonable caller must handle and could not have checked first. `exists()` is
that check, which is why a missing path earned no clause.

Cleanup follows the same idea. With no destructors, a class that must be closed says so by
implementing `AutoCloseable`, and the caller says it back in a `try`-with-resources header.
