Exceptions and try / catch / finally
RHP uses structured exception handling for recoverable runtime faults:
class RecordNotFoundException extends Exception {}
function findUser(int $id): dict {
if ($id <= 0) {
throw new RecordNotFoundException("Invalid user ID {$id}");
}
return Arr::dict(['id' => $id, 'name' => 'Valid User']);
}
try {
$user = findUser(-1);
} catch (RecordNotFoundException $e) {
Log::warning("User not found: " . $e->getMessage());
} catch (Exception $e) {
Log::error("Unexpected fault: " . $e->getMessage());
} finally {
// Executes regardless of exception state
Log::info("Execution check complete");
}
Fatal Errors vs Catchable Exceptions
RHP distinguishes between recoverable exceptions and engine safety invariants:
| Error Type | Behavior | Example |
|---|---|---|
| Exception | Catchable via try / catch |
Network timeout, file missing, parsing error |
| TypeError | Boundary failure at compile/runtime | Assigning string to declared int $x |
| Fatal Invariant | Immediate worker panic (request dropped) | Cyclic closure capture, extending static facade |
When a worker task panics on a fatal invariant, the parent process remains completely healthy and uncorrupted, resetting the request arena in $O(1)$ time.
Exact Source Spans & Stack Traces
In Development Mode (rhp run dev), errors attach directly to exact source spans (Span { file_id, start, end, line, col }) generated by the Logos DFA lexer, yielding pinpoint stack traces:
[RHP Runtime Error] VariableTypeMismatch: Incompatible type assignment
--> src/OrderService.rhp:42:15
|
42 | int $sum = "invalid_string";
| ^^^^^^^^^^^^^^^^ Expected type 'int', found 'string'
|