Shells are rarely CPU bound, so some perf overhead is acceptable. But shells may be used to recover badly broken systems. If fork or pipe fails, most programs are OK to abort, but a shell may be the user's last hope, so has to keep going.
For example, if pipe() fails, it's probably due to fd exhaustion. If your system is in that state, the best thing to do is immediately unwind whatever is executing, and put the user back at the prompt. fish uses ad-hoc error codes (reflecting its C legacy) instead of exceptions, though it uses RAII for cleanup. Your question made me realize that fish needs a better abstraction here; at least use `nodiscard`.
The story is different for script errors [1]. If the user forgets to (say) close a quote in a config file, fish will print the line, a caret, and a backtrace to the executing script. A lot of effort has gone into providing good error messages with many special cases detected. The parser also knows how to recover and keep going; I think Fabien would approve.
1: https://github.com/fish-shell/fish-shell/blob/225470493b3cd1...