
Laravel rescue(): Controlled Fallback Without a Large try/catch
Use rescue() for fallback values, automatic exception reporting, conditional report callbacks, and concise handling of optional operations without a large try/catch block.
Sometimes an exception should not stop the entire request. An optional service may fail while the page can still render its primary data.
When a try/catch exists only to return a fallback value and optionally report the exception, Laravel provides the concise rescue() helper.
Replace a Large try/catch Used Only for Fallback
try { $data = $service->load();} catch (Throwable $exception) { report($exception); $data = null;}$data = rescue( fn () => $service->load(), rescue: null, report: true,);rescue() executes the callback. On success, its result is returned. If a throwable occurs, Laravel may send it through the normal exception reporting path and return the configured fallback value.
The intent is direct: try to load the data, and return null if that operation fails.
Without a Second Argument, the Fallback Is Already null
$profile = rescue( fn () => $profileService->load($userId),);If the callback succeeds, $profile contains its return value. If a throwable occurs, rescue() returns null by default.
Exception reporting is also enabled by default.
The Fallback Can Be More Than null
$settings = rescue( fn () => $settingsService->load($userId), rescue: [], report: true,);Choose a fallback that keeps the code after rescue() predictable. If the successful result is an array, returning [] may be clearer than switching to an unrelated type.
The Fallback Can Be Calculated by a Callback
$rates = rescue( fn () => $ratesService->loadCurrent(), rescue: fn (Throwable $exception) => $cache->get('rates:last-known', []), report: true,);The second argument may be a callback. Laravel passes the caught Throwable to it, so fallback logic can inspect the failure or simply return a last-known value from cache.
This is useful when the fallback is not a single constant but still remains a small and understandable operation.
report: true Keeps the Failure Visible
$preview = rescue( fn () => $previewService->build($document), rescue: null, report: true,);A fallback does not mean the failure should disappear. With report: true, the throwable is sent through Laravel's normal exception handler while the current request continues with the fallback result.
This is one of the strongest uses of rescue(): the user flow can continue while the technical problem stays visible to monitoring and logs.
Use report: false Only for Truly Expected Failures
$value = rescue( fn () => $optionalService->load(), rescue: null, report: false,);Reporting Can Be Decided Per Throwable
$value = rescue( fn () => $service->load(), rescue: null, report: fn (Throwable $exception): bool => $exception instanceof RuntimeException,);The report argument may be a callback instead of a boolean. It receives the throwable and returns a boolean, allowing reporting only for selected failures.
rescue() Catches Throwable, Not Only Exception
The current Laravel implementation catches Throwable, which is broader than a normal Exception.
That is why the scope around rescue() should stay narrow. Wrapping a large block can accidentally turn a serious programming failure into an ordinary null.
Wrap Only the Operation Where Fallback Is Acceptable
$result = rescue(function () use ($order) { $this->validateOrder($order); $this->chargeCustomer($order); $this->reserveStock($order); $this->sendAnalytics($order); return $order;}, rescue: null);$order = $checkoutService->complete($order); $analytics = rescue( fn () => $analyticsService->send($order), rescue: null, report: true,);A critical business workflow should not silently collapse into a fallback. If charging a customer or reserving stock fails, the application normally needs explicit handling or the exception should propagate.
Optional analytics after the main operation succeeds can be a better candidate for rescue() when its failure must not break the user response.
rescue() Does Not Retry the Operation
$result = retry( 3, fn () => $remoteService->load(), 200,);rescue() and retry() solve different problems. rescue() runs the callback once and provides a controlled result after failure. retry() is designed to attempt the operation again.
They may be combined when retrying is genuinely appropriate for that operation.
When Plain try/catch Is Still Better
- when different exception types require different actions;
- when recovery requires several steps;
- when resource cleanup or a
finallyblock is needed; - when the failure is an important part of the business workflow rather than just a reason to return a fallback;
- when an explicit
try/catchcommunicates complex behavior more clearly.
A good rescue() call does not hide failure. It defines a clear boundary: this specific operation may fail, this is the safe fallback result, and this is the rule for whether the throwable should be reported.




