Silent Failure Analyzer
| Analyzer ID | Category | Severity | Time To Fix |
|---|---|---|---|
silent-failure | ⚡ Best Practices | High | 20 minutes |
Detects empty catch blocks and error suppression that hide failures, making debugging impossible and masking critical production issues.
What This Checks
The Silent Failure Analyzer identifies three dangerous error handling patterns that hide exceptions and errors, making bugs impossible to debug in production:
Detected Patterns:
Empty Catch Blocks (High Severity)
catch (Exception $e) { }- Completely swallows exceptions- No logging, no rethrow, no error handling
Catch Without Logging or Rethrow (Medium Severity)
catch (Exception $e) { return null; }- Handles exception but doesn't log- Missing
Log::error(),report(), orthrow - Silently fails without any trace
Error Suppression Operator (Medium Severity)
@file_get_contents($path)- Hides all errors and warnings@mysql_query($sql)- Suppresses error messages- Makes debugging impossible when things go wrong
Why It Matters
Silent failures are one of the most dangerous anti-patterns in production applications:
1. Impossible Debugging
When exceptions are swallowed silently, you have no way to know what went wrong:
Before (❌):
class PaymentProcessor
{
public function charge($amount)
{
try {
$result = $this->gateway->charge($amount);
return $result;
} catch (Exception $e) {
// Silent failure - payment fails but no one knows
return false;
}
}
}
// Production: Customers can't checkout, no logs, no alerts, no idea whyAfter (✅):
class PaymentProcessor
{
public function charge($amount)
{
try {
$result = $this->gateway->charge($amount);
return $result;
} catch (Exception $e) {
// Log the exception for debugging
Log::error('Payment processing failed', [
'amount' => $amount,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
// Report to error tracking service
report($e);
// Still return false, but now we know what happened
return false;
}
}
}2. Hidden Security Vulnerabilities
Silent failures can mask security issues:
Before (❌):
class AuthService
{
public function validateToken($token)
{
try {
return JWT::decode($token, $this->key);
} catch (Exception $e) {
// Token validation fails silently
// Attacker has no idea their attack failed
return null;
}
}
}After (✅):
class AuthService
{
public function validateToken($token)
{
try {
return JWT::decode($token, $this->key);
} catch (Exception $e) {
// Log potential security issue
Log::warning('Invalid JWT token attempted', [
'token' => substr($token, 0, 20) . '...',
'ip' => request()->ip(),
]);
return null;
}
}
}3. Data Loss and Corruption
Without error reporting, data operations fail silently:
Before (❌):
class DataImporter
{
public function import($file)
{
try {
$data = $this->parse($file);
$this->save($data);
} catch (Exception $e) {
// Import fails, data is lost, no one knows
}
}
}After (✅):
class DataImporter
{
public function import($file)
{
try {
$data = $this->parse($file);
$this->save($data);
} catch (Exception $e) {
Log::error('Data import failed', [
'file' => $file,
'error' => $e->getMessage(),
]);
// Optionally rethrow to bubble up
throw new ImportException('Failed to import data', 0, $e);
}
}
}How to Fix
Quick Fix (~5 minutes per catch block)
For simple cases, add logging to catch blocks:
Before (❌):
class OrderService
{
public function createOrder($data)
{
try {
return Order::create($data);
} catch (Exception $e) {
return null;
}
}
}After (✅):
class OrderService
{
public function createOrder($data)
{
try {
return Order::create($data);
} catch (Exception $e) {
Log::error('Order creation failed', [
'data' => $data,
'error' => $e->getMessage(),
]);
return null;
}
}
}Steps:
- Add
use Illuminate\Support\Facades\Log;at the top - Call
Log::error()in catch block with context - Include relevant data for debugging
- Consider using
report($e)for error tracking services
Proper Fix (~20 minutes per class)
For production code, implement comprehensive error handling:
Before (❌):
class EmailService
{
public function send($user, $message)
{
try {
Mail::to($user)->send($message);
} catch (Exception $e) {
// Silent failure
}
}
public function readTemplate($path)
{
return @file_get_contents($path);
}
}After (✅):
class EmailService
{
public function send($user, $message)
{
try {
Mail::to($user)->send($message);
} catch (Exception $e) {
// Log with full context
Log::error('Email sending failed', [
'user_id' => $user->id,
'email' => $user->email,
'message_class' => get_class($message),
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
// Report to error tracking (Sentry, Bugsnag, etc.)
report($e);
// Optionally rethrow for critical failures
if ($message instanceof CriticalNotification) {
throw $e;
}
}
}
public function readTemplate($path)
{
// Replace error suppression with proper error handling
if (!file_exists($path)) {
Log::warning('Email template not found', ['path' => $path]);
return $this->getDefaultTemplate();
}
$content = file_get_contents($path);
if ($content === false) {
Log::error('Failed to read email template', ['path' => $path]);
throw new TemplateException("Cannot read template: {$path}");
}
return $content;
}
}Steps:
- Add comprehensive logging with context
- Integrate error tracking (Sentry, Bugsnag, Rollbar)
- Replace
@operator with proper error handling - Add graceful fallbacks where appropriate
- Rethrow critical exceptions
- Document expected exceptions
ShieldCI Configuration
To configure whitelists, publish the config:
php artisan vendor:publish --tag=shieldci-configThen in config/shieldci.php:
'analyzers' => [
'best-practices' => [
'enabled' => true,
'silent-failure' => [
// Directories to skip (tests can have empty catches)
'whitelist_dirs' => [
'tests',
'database/seeders',
'database/factories',
],
// Class patterns to skip
'whitelist_classes' => [
'*Test', // PHPUnit tests
'*TestCase', // Test base classes
'*Seeder', // Database seeders
'DatabaseSeeder', // Main seeder
],
// Exception types that are expected and can be safely caught
'whitelist_exceptions' => [
'ModelNotFoundException', // Expected when using findOrFail
'NotFoundException', // Custom not found exceptions
'NotFoundHttpException', // HTTP 404 exceptions
'ValidationException', // Validation failures
],
// Functions where @ operator is acceptable (supports namespaced: @\unlink())
'whitelist_error_suppression_functions' => [
'unlink', // File might not exist
'fopen', // File might not be readable
'file_get_contents', // File might not exist
'mkdir', // Directory might exist
'rmdir', // Directory might not exist
],
// Static methods where @ operator is acceptable (e.g., @Storage::delete())
// Wildcards supported: 'Storage::*', '*::delete'
'whitelist_error_suppression_static_methods' => [
'Storage::delete', // File might not exist
'Storage::deleteDirectory', // Directory might not exist
'File::delete', // File might not exist
'File::deleteDirectory', // Directory might not exist
],
// Instance methods where @ operator is acceptable (e.g., @$file->delete())
// Matches by method name only. Wildcards supported: 'remove*'
'whitelist_error_suppression_instance_methods' => [
'delete', // Resource deletion
'close', // File handle closing
'unlink', // File unlinking
],
],
],
],Error Suppression Whitelist Details
The error suppression whitelist supports three types of calls:
| Call Type | Example | Config Key |
|---|---|---|
| Function calls | @unlink($path), @\unlink($path) | whitelist_error_suppression_functions |
| Static method calls | @Storage::delete($path) | whitelist_error_suppression_static_methods |
| Instance method calls | @$file->delete() | whitelist_error_suppression_instance_methods |
Wildcard patterns are supported in all whitelist keys:
Storage::*- matches any method on Storage*::delete- matches delete on any classremove*- matches removeFile, removeAll, etc.
Dynamic calls are always flagged for security reasons:
@$func($path)- dynamic function call@$class::method()- dynamic class@$obj->$method()- dynamic method name
References
- Laravel Error Handling - Official Laravel documentation
- PSR-3 Logger Interface - Standard logging interface
- Sentry for Laravel - Error tracking integration
- Bugsnag Laravel Integration - Alternative error tracking
- Logging Best Practices - Comprehensive logging guide
- Exception Handling Patterns - Martin Fowler on exceptions
Related Analyzers
- Service Container Resolution - Avoid manual container resolution that hides dependencies
- Debug Mode Analyzer - Detects debug mode enabled in production