# Request error handling

## Browser requests

Errors raised while handling a regular browser page must keep the application usable. Controllers redirect to a safe page through `Flash::redirect()` and show a Polish alert. Form errors preserve only the input that is safe to display again. Authorization failures may use a modal when the user must clearly notice that the operation is unavailable.

Raw `die()` or `exit()` arguments are not used to display request, CSRF, authorization, validation, missing-record, or state-conflict errors. A bare termination remains acceptable after a successful `Location` response, after a file response has been fully streamed, or in another response helper that has already produced the complete response.

One-time messages are stored in Flash state. Query parameters are not used as flags that cause an alert to reappear after a refresh.

## Non-page endpoints

JSON endpoints return an appropriate HTTP status and a structured JSON body. Restricted machine endpoints may return only an HTTP status with an empty body. Command-line scripts write failures to standard error and use a non-zero process exit code.

## Bootstrap failures

A database connection failure cannot redirect through Flash because the destination would immediately require the same unavailable database and create a redirect loop. Browser requests therefore return HTTP 503 with `Retry-After` and render `application/view/_errors/database_unavailable.php` directly. The technical exception is written only to the server error log. CLI processes receive a short stderr message and exit code 1.

## Verification

Run `composer test:request-error-handling`. The regression test rejects `die` or `exit` arguments in controllers and the HTTP security helper, checks the audited browser methods for `Flash::redirect()`, and verifies the safe database-unavailable response.
