You inherit a codebase. Every API call wrapped in try-catch. Every exception swallowed. Every error returns an empty collection.
And nobody knows when things break.
The Anti-Pattern
Here’s what I found in a legacy integration:
public function getProducts(): Collection
{
try {
$response = Http::get($this->apiUrl . '/products');
return collect($response->json('data'));
} catch (Exception $e) {
// "Handle" the error by pretending it didn't happen
return collect([]);
}
}
What happens when the API is down? Nothing. The method returns empty. The caller thinks there are no products. The error disappears into the void.
Weeks later, you’re debugging why users see blank pages. The real error? A 500 from the API three weeks ago that nobody noticed because the logs were clean.
Let It Fail
The fix is brutal: delete the try-catch.
public function getProducts(): Collection
{
$response = Http::get($this->apiUrl . '/products');
return collect($response->json('data'));
}
Now when the API fails, the exception bubbles up. Your error tracking (Sentry, Bugsnag, whatever) catches it. You get alerted. You fix it.
But What About Graceful Degradation?
If you genuinely want to fail gracefully, make it explicit:
public function getProducts(): Collection
{
try {
$response = Http::timeout(5)->get($this->apiUrl . '/products');
return collect($response->json('data'));
} catch (RequestException $e) {
// Log it so you know it happened
Log::warning('Product API failed', [
'error' => $e->getMessage(),
'url' => $this->apiUrl
]);
// Return cached/stale data as fallback
return Cache::get('products.fallback', collect([]));
}
}
Now you’re:
- Logging the failure (visibility)
- Using a fallback strategy (resilience)
- Not silently lying to callers (honesty)
When to Catch
Catch exceptions when you have a recovery strategy:
- Retry logic: API hiccup? Try again.
- Fallback data: Cache, defaults, partial results.
- User-facing context: Transform technical errors into user messages.
If you’re just catching to return empty, you’re hiding problems.
Laravel’s Global Exception Handler
Laravel already has a system for this. Exceptions bubble to App\Exceptions\Handler, where you can:
// app/Exceptions/Handler.php
public function register()
{
$this->reportable(function (RequestException $e) {
// Log to Sentry/Slack/etc
});
$this->renderable(function (RequestException $e) {
return response()->json([
'error' => 'External service unavailable'
], 503);
});
}
Now all API failures follow the same pattern. No more scattered try-catch blocks pretending everything’s fine.
The Takeaway
Empty catch blocks are lies. If you can’t handle the error meaningfully, let it bubble. Your future self — the one debugging at 2 AM — will thank you.









