Laravel Debugging Guide
Overview
Debugging in Laravel requires understanding the framework's conventions, lifecycle, and tooling ecosystem. This guide provides a systematic approach to diagnosing and resolving Laravel issues, from simple configuration problems to complex runtime errors.
Key Principles:
-
Always check logs first (storage/logs/laravel.log )
-
Use appropriate tools for the environment (development vs production)
-
Follow Laravel conventions - most errors stem from convention violations
-
Isolate the problem layer (routing, middleware, controller, model, view)
Common Error Patterns
Class and Namespace Errors
Class 'App\Http\Controllers\Controller' not found
// Problem: Missing base controller extension or incorrect namespace // Solution: Ensure proper namespace and inheritance namespace App\Http\Controllers;
use Illuminate\Routing\Controller as BaseController;
class YourController extends BaseController { // ... }
Target class [ControllerName] does not exist
// Check routes/web.php or routes/api.php // Laravel 8+ requires full namespace or route groups use App\Http\Controllers\UserController;
Route::get('/users', [UserController::class, 'index']);
// Or use route group with namespace Route::namespace('App\Http\Controllers')->group(function () { Route::get('/users', 'UserController@index'); });
Database Errors (SQLSTATE)
SQLSTATE[HY000] [1045] Access denied
Check .env database credentials
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=your_database DB_USERNAME=your_username DB_PASSWORD=your_password
Clear config cache after changes
php artisan config:clear
SQLSTATE[42S02] Table not found
Run migrations
php artisan migrate
Check migration status
php artisan migrate:status
Fresh database with seeders
php artisan migrate:fresh --seed
QueryException with foreign key constraint
// Check migration order - parent tables must exist first // Use Schema::disableForeignKeyConstraints() for fresh migrations Schema::disableForeignKeyConstraints(); // ... your schema changes Schema::enableForeignKeyConstraints();
Route Errors
404 Not Found - Route does not exist
List all registered routes
php artisan route:list
Check specific route
php artisan route:list --name=users
Clear route cache
php artisan route:clear
MethodNotAllowedHttpException
// Wrong HTTP method for route // Check route definition matches request method Route::post('/submit', [FormController::class, 'store']); // Expects POST Route::put('/update/{id}', [FormController::class, 'update']); // Expects PUT
// For HTML forms using PUT/PATCH/DELETE <form method="POST" action="/update/1"> @csrf @method('PUT') </form>
View Errors
View [name] not found
Check view file exists at correct path
resources/views/users/index.blade.php for view('users.index')
Clear view cache
php artisan view:clear
Undefined variable in view
// Ensure variable is passed from controller return view('users.index', [ 'users' => $users, 'total' => $total, ]);
// Or use compact() return view('users.index', compact('users', 'total'));
Middleware Issues
TokenMismatchException (CSRF)
// Include @csrf in all POST/PUT/DELETE forms <form method="POST" action="/submit"> @csrf <!-- form fields --> </form>
// For AJAX requests, include token in headers $.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });
// Exclude routes from CSRF if needed (VerifyCsrfToken middleware) protected $except = [ 'stripe/', 'webhook/', ];
Unauthenticated / 403 Forbidden
Check middleware applied to route
php artisan route:list --columns=uri,middleware
Verify auth guards in config/auth.php
Check Gate/Policy definitions
Queue and Job Failures
Job failed without reason
Check failed jobs table
php artisan queue:failed
Retry specific job
php artisan queue:retry <job-id>
Retry all failed jobs
php artisan queue:retry all
Clear failed jobs
php artisan queue:flush
Jobs not processing
Ensure queue worker is running
php artisan queue:work
Check queue connection in .env
QUEUE_CONNECTION=redis
Restart queue after code changes
php artisan queue:restart
Cache and Session Problems
Session not persisting
Clear session files
php artisan session:clear
Check session driver
SESSION_DRIVER=file # or redis, database, etc.
Verify storage permissions
chmod -R 775 storage/ chown -R www-data:www-data storage/
Config/cache showing old values
Clear all caches
php artisan optimize:clear
Or individually
php artisan config:clear php artisan cache:clear php artisan route:clear php artisan view:clear
Debugging Tools
Laravel Telescope
The most comprehensive debugging tool for Laravel development.
Installation:
composer require laravel/telescope --dev php artisan telescope:install php artisan migrate
Access: Navigate to /telescope in your browser
Key Features:
-
Requests: View all HTTP requests with headers, parameters, response
-
Exceptions: Stack traces and context for all errors
-
Queries: All SQL queries with execution time and bindings
-
Jobs: Queue job payloads, execution times, failures
-
Logs: All log entries in real-time
-
Mail: Preview sent emails with content
-
Cache: Track cache hits, misses, and operations
-
Events: All dispatched events and listeners
Security: Limit access in production via TelescopeServiceProvider :
protected function gate() { Gate::define('viewTelescope', function ($user) { return in_array($user->email, [ 'admin@example.com', ]); }); }
Laravel Debugbar
Quick inline debugging for web pages.
Installation:
composer require barryvdh/laravel-debugbar --dev
Features:
-
Query count and execution time
-
Route information
-
View rendering time
-
Memory usage
-
Timeline visualization
Ray by Spatie
Desktop app for non-intrusive debugging.
Installation:
composer require spatie/laravel-ray --dev
Usage:
ray($variable); // Send to Ray app ray($var1, $var2)->green(); // Color coded ray()->measure(); // Start timer ray()->pause(); // Pause execution ray()->showQueries(); // Show all queries
Built-in Debugging Functions
// Dump and Die - stops execution dd($variable); dd($user, $posts, $comments);
// Dump without dying dump($variable);
// Dump, Die, Debug (with extra info) ddd($variable);
// In Blade templates @dd($variable) @dump($variable)
// Log to file Log::debug('Message', ['context' => $data]); Log::info('User logged in', ['user_id' => $user->id]); Log::error('Payment failed', ['order' => $order]);
// Log levels: emergency, alert, critical, error, warning, notice, info, debug
Artisan Tinker
Interactive REPL for testing code:
php artisan tinker
Test Eloquent queries
User::where('active', true)->count() => 42
Test relationships
$user = User::find(1) $user->posts()->count()
Test services
app(UserService::class)->processUser($user)
The Four Phases of Laravel Debugging
Phase 1: Root Cause Investigation
Check Logs First:
View latest errors
tail -f storage/logs/laravel.log
Search for specific errors
grep -i "error|exception" storage/logs/laravel.log | tail -50
Check with specific date
cat storage/logs/laravel-2025-01-11.log
Verify Configuration:
Check current environment
php artisan env
Dump all config values
php artisan config:show
Check specific config
php artisan config:show database php artisan config:show queue
Validate .env file
php artisan config:clear && php artisan config:cache
Check Application State:
Check if app is in maintenance mode
php artisan up # or down
Check scheduled tasks
php artisan schedule:list
Check registered routes
php artisan route:list
Check registered events
php artisan event:list
Phase 2: Pattern Analysis
Compare with Laravel Conventions:
Component Convention Common Mistake
Models App\Models\User (singular) Using plural names
Controllers UserController
Missing Controller suffix
Migrations create_users_table (plural) Singular table names
Views resources/views/users/index.blade.php
Wrong directory structure
Routes RESTful naming Non-standard HTTP methods
Check for Common Anti-patterns:
// BAD: N+1 query problem $users = User::all(); foreach ($users as $user) { echo $user->profile->bio; // Query per iteration! }
// GOOD: Eager loading $users = User::with('profile')->get(); foreach ($users as $user) { echo $user->profile->bio; // No additional queries }
Verify Dependencies:
Check composer dependencies
composer show
Check for package conflicts
composer diagnose
Update autoloader
composer dump-autoload
Phase 3: Hypothesis and Testing
Create Focused Tests:
// tests/Feature/UserTest.php use Tests\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase { use RefreshDatabase;
public function test_user_can_be_created()
{
$response = $this->post('/users', [
'name' => 'Test User',
'email' => 'test@example.com',
]);
$response->assertStatus(201);
$this->assertDatabaseHas('users', [
'email' => 'test@example.com',
]);
}
}
Run Tests:
Run all tests
php artisan test
Run specific test file
php artisan test tests/Feature/UserTest.php
Run specific test method
php artisan test --filter=test_user_can_be_created
Run with coverage
php artisan test --coverage
Use Pest (if installed)
./vendor/bin/pest
Test in Isolation:
Test artisan commands
php artisan tinker
User::factory()->create() $user->notify(new WelcomeNotification)
Test queue jobs synchronously
QUEUE_CONNECTION=sync php artisan your:command
Test with fresh database
php artisan migrate:fresh --seed && php artisan test
Phase 4: Implementation and Verification
Apply Fix:
Clear all caches after changes
php artisan optimize:clear
Rebuild autoloader
composer dump-autoload
Restart queue workers
php artisan queue:restart
Re-cache for production
php artisan config:cache php artisan route:cache php artisan view:cache
Verify Fix:
Run full test suite
php artisan test
Check for new errors in logs
tail -f storage/logs/laravel.log
Monitor queue processing
php artisan queue:work --verbose
Check application health
php artisan about
Quick Reference Commands
Clearing and Caching
Clear everything
php artisan optimize:clear
Individual clears
php artisan config:clear # Config cache php artisan route:clear # Route cache php artisan view:clear # Compiled views php artisan cache:clear # Application cache php artisan event:clear # Event cache
Rebuild caches (production)
php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache
Database Commands
Migration status
php artisan migrate:status
Run migrations
php artisan migrate
Rollback last migration
php artisan migrate:rollback
Fresh database with seeds
php artisan migrate:fresh --seed
Show database info
php artisan db:show
Open database CLI
php artisan db
Queue Commands
Check failed jobs
php artisan queue:failed
Retry failed job
php artisan queue:retry <id>
Retry all failed
php artisan queue:retry all
Clear failed jobs
php artisan queue:flush
Work queue
php artisan queue:work --verbose
Restart workers (after code changes)
php artisan queue:restart
Route Commands
List all routes
php artisan route:list
Filter by name
php artisan route:list --name=api
Filter by path
php artisan route:list --path=users
Show columns
php artisan route:list --columns=method,uri,name,action
Debugging Commands
Show app info
php artisan about
Interactive shell
php artisan tinker
Check environment
php artisan env
Show config
php artisan config:show
Schedule list
php artisan schedule:list
Environment-Specific Debugging
Development
APP_DEBUG=true APP_ENV=local LOG_CHANNEL=stack LOG_LEVEL=debug
Enable all debugging tools:
-
Laravel Telescope
-
Laravel Debugbar
-
Ray
Production
APP_DEBUG=false APP_ENV=production LOG_CHANNEL=stack LOG_LEVEL=error
Use:
-
Error tracking services (Sentry, Bugsnag)
-
Log aggregation (Papertrail, Loggly)
-
APM tools (New Relic, Scout)
Never expose detailed errors in production.
Logging Best Practices
Structured Logging
// Include context with every log Log::info('Order processed', [ 'order_id' => $order->id, 'user_id' => $order->user_id, 'total' => $order->total, 'items_count' => $order->items->count(), ]);
// Use appropriate log levels Log::debug('Detailed debugging info'); // Development only Log::info('Normal operational events'); // User actions, etc. Log::warning('Unusual but not errors'); // Deprecated usage, etc. Log::error('Runtime errors'); // Exceptions, failures Log::critical('System is unusable'); // Database down, etc.
Custom Log Channels
// config/logging.php 'channels' => [ 'payments' => [ 'driver' => 'daily', 'path' => storage_path('logs/payments.log'), 'level' => 'info', 'days' => 30, ], ],
// Usage Log::channel('payments')->info('Payment processed', ['id' => $payment->id]);
Additional Resources
-
Laravel Official Documentation
-
Laravel Telescope Documentation
-
Laravel Debugbar GitHub
-
Spatie Ray Documentation
-
Laravel News - Debugging Articles
-
Sentry Laravel Integration