Use in Laravel projects when encountering any bug, test failure, unexpected behavior, N+1 queries, queue issues, or cache problems. Provides Laravel-specific debugging tools and workflows. In Laravel codebases, invoke this instead of superpowers:systematic-debugging. Trigger on any error, 500, unexpected output, slow query, or 'why is X not working' in a Laravel project.
How this skill is triggered — by the user, by Claude, or both
Slash command
/laravel-vue-superpowers:laravel-debuggingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Find root cause before touching production code.
Find root cause before touching production code. In Laravel: read the logs and run tinker before guessing.
NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST
Random dd() placement is not debugging. It's thrashing.
Start here. Always. Before proposing any fix.
# Laravel log (the first place to check)
tail -n 100 storage/logs/laravel.log
tail -f storage/logs/laravel.log # live tail
# If using daily logs
tail -n 100 storage/logs/laravel-$(date +%Y-%m-%d).log
Read the full stack trace — not just the first line. The actual cause is often several frames deep.
For web/API issues:
message or errors key?# Quick API test
curl -s -w "\n%{http_code}" http://localhost/api/endpoint | tail -2
php artisan tinker
Tinker has full app context — models, facades, services, config, DB. Use it to:
# Test a model query
$user = App\Models\User::find(1);
$user->posts()->count();
# Check what a factory generates
App\Models\Post::factory()->make()->toArray();
# Test a service
$svc = app(App\Services\PaymentService::class);
$svc->charge(100, 'EUR');
# Check a config value
config('services.stripe.key');
# Test an event dispatch
event(new App\Events\UserRegistered(User::find(1)));
# Check a route resolution
app('router')->getRoutes()->getByName('dashboard');
git diff HEAD~3..HEAD --stat # what changed recently
git log --oneline -10 # recent commits
git diff HEAD~1 # last commit diff
php artisan config:clear
php artisan cache:clear
php artisan view:clear
php artisan route:clear
php artisan event:clear
# All at once:
php artisan optimize:clear
If the bug disappears after clearing caches → the bug was a stale cache. Now understand why it got stale.
php artisan route:list # see all routes
php artisan route:list --name=user # filter by name
php artisan route:list --path=api/users # filter by path
Check middleware stack on the route — authorization failures often come from middleware, not the controller.
N+1 queries — the most common Laravel performance bug:
// Add to AppServiceProvider::boot() temporarily:
\DB::listen(function ($query) {
if ($query->time > 100) { // queries slower than 100ms
\Log::info('Slow query', ['sql' => $query->sql, 'bindings' => $query->bindings, 'time' => $query->time]);
}
});
// In tinker — count queries for a request:
\DB::enableQueryLog();
// ... run the code that seems slow ...
\DB::getQueryLog(); // shows all executed queries with times
Unexpected model values:
php artisan tinker
# Check actual DB vs model cast vs what you expect
$post = App\Models\Post::find(1);
$post->toArray(); # all attributes + casts applied
$post->getRawOriginal(); # raw DB values before casting
$post->getDirty(); # unsaved changes
Migration / schema issues:
php artisan model:show User # schema, relationships, attributes
php artisan migrate:status # which migrations ran
php artisan schema:dump # current schema
# Run one job manually (don't rely on the worker being up)
php artisan queue:work --once
# Watch queue in real time
php artisan queue:work --verbose
# See failed jobs
php artisan queue:failed
# Retry a specific failed job
php artisan queue:retry <id>
# Retry all failed jobs
php artisan queue:retry all
Check failed_jobs table:
php artisan tinker
# See the exception that caused the failure:
DB::table('failed_jobs')->latest()->first();
php artisan tinker
# Test a policy
$user = App\Models\User::find(1);
$post = App\Models\Post::find(1);
$user->can('update', $post); // true/false
Gate::forUser($user)->inspect('update', $post); // detailed result
php artisan config:show database # check DB config
php artisan config:show mail # check mail config
php artisan env # check APP_ENV
# Verify .env is being loaded
php artisan tinker
env('APP_KEY');
config('app.key'); # these should match
# Test sending mail without a real SMTP server
# Set MAIL_MAILER=log in .env — mail goes to laravel.log
php artisan tinker
Mail::to('[email protected]')->send(new App\Mail\WelcomeMail());
# Then check storage/logs/laravel.log
If Phase 1-2 didn't reveal the cause, add targeted logging:
// In the code under investigation:
\Log::info('Checkpoint', [
'user_id' => $user->id,
'payload' => $data,
'result' => $result,
]);
Trace boundary by boundary:
Telescope (if installed at /telescope):
laravel-tdd skill).php artisan test — the new test must pass, no existing tests must break.| Symptom | Likely Root Cause |
|---|---|
500 with no message | Check storage/logs/laravel.log |
419 Page Expired | CSRF token missing — check form has @csrf |
401 Unauthenticated | Token missing/expired or wrong guard in route |
403 Forbidden | Policy authorize() returns false |
422 Unprocessable | Validation failed — check errors in response body |
| N+1 queries | Missing with() on relationship — check query log |
| Stale data | Config/route/view cache — run php artisan optimize:clear |
| Job not running | Queue worker not started, or QUEUE_CONNECTION is sync |
| Mail not sent | MAIL_MAILER=log? Check laravel.log instead of inbox |
| Env var not loading | .env change needs php artisan config:clear |
| Wrong value from config | Using env() outside config file (cached incorrectly) |
| Thought | What to do instead |
|---|---|
| "Let me just dd() everywhere" | Read the log first, then place one targeted dd() |
| "Probably a cache issue, let me clear" | Investigate first, clear caches as a hypothesis test |
| "It works on my machine" | Compare .env files, run php artisan config:show |
| "Let me just change X and see" | Form a hypothesis first, then test it minimally |
| "The queue must be broken" | Run php artisan queue:work --once to verify |
Telescope setup and query debugging → references/telescope-guide.md
When you hit a RED in a Pest 4 test for one of these stack-specific symptoms, this table maps error signature → root cause → concrete fix. Use it AFTER you've confirmed the symptom (RCA discipline still applies — these are pattern-matching shortcuts, not skip-the-investigation excuses).
expected array to contain 'X' with a long second argError signature: Failed asserting that array contains 'should include foo'.
Root cause: Pest's toContain(...$needles) is variadic. The "message" you passed as arg #2 became a second needle — the assertion now requires both 'X' AND 'should include foo' to be present in the array.
Fix:
expect($response->json('items'))
->toContain('foo')
->because('should include foo');
Sibling-canon: see laravel-tdd skill §"Variadic-Expectation Trap" for the full pattern.
Using $this when not in object context in view renderingError signature: PHP fatal: Using $this when not in object context raised from a compiled Blade template.
Root cause: view()->with(['this' => $obj]) — $this is reserved by PHP and Blade refers to the renderer's render-context. Your $obj is unreachable in the template.
Fix: rename the key to something meaningful and access it accordingly in the view:
return view('ai.popover', ['ai' => $aiAgent, 'surface' => 'editor']);
// view: {{ $ai->dispatch('foo') }}
Other reserved keys to avoid: loop, errors, __env, app, attributes, component, slot.
Error signature: runtime exception or type error naming a method or composable that "should exist" per the plan-doc but doesn't — e.g., useHttp().fetchAll(), computeed(...), refrence(...).
Root cause: hallucinated API name. Common forms:
useHttp (was not in v2), but useHttp().fetchAll() does not exist — the actual API is router.visit() or useForm() for mutations.computeed instead of computed, refrence instead of ref.Fix for Inertia: verify the composable exists in the installed package:
grep -r "export.*useHttp\|export.*function useHttp" node_modules/@inertiajs/vue3/dist/
Use useForm for form submissions and router.visit for programmatic navigation — not raw fetch or fabricated helpers.
Fix for Vue: check the Composition API import:
// Correct
import { computed, ref } from 'vue';
Verification of Inertia v3 surface:
grep -E "^export (const|function|class)" node_modules/@inertiajs/vue3/dist/index.js | head -20
Invoke laravel-inertia-specialist agent for Inertia surface; laravel-vue3-specialist for Vue Composition API surface.
401 Unauthorized from external API in Pest testError signature: test failure shows external HTTP 401 (Anthropic, Stripe, etc.) coming from real production endpoints.
Root cause: phpunit.xml blanks the API key for safety (<env name="ANTHROPIC_API_KEY" value=""/>), but your test invoked a code path that calls the real API. Bus::fake() doesn't intercept synchronous static calls (e.g., AiAgentRunner::run(...) if it's not dispatched as a Job).
Fix options:
Mockery::mock('alias:App\AiAgentRunner')->shouldReceive('run')->andReturn(...)arch() test to pin method existence + signature, run integration manuallyBus::fake() + Bus::assertDispatched(...)Decision: depends on intent — if the test should verify the code path runs without invoking the API, Mockery is best. If the test should verify Job dispatch, refactor + Bus::fake.
Route [foo.bar] not definedError signature: test fails on $this->get(route('foo.bar')) or similar.
Root cause: route name mismatch — typo, wrong file, missing ->name('foo.bar') call, or controller renamed without route update.
Fix: verify the route exists and is named correctly:
php artisan route:list --name=foo
If the route exists with a different name, fix the test or the route. If the route doesn't exist, add it.
Cannot use object of type Model as array / array_filter expects parameter 1 to be array, Collection givenError signature: PHP type juggling error in test or runtime where code treats a model as an array, or treats a collection as a single model.
Root cause: relationship mismatch. hasOne returns a single model; hasMany returns a Collection. Calling ->first() on a hasOne is redundant; iterating a hasOne result errors.
Fix: check the relationship method in the parent model:
php artisan model:show User --no-relations # or inspect app/Models/User.php
Adjust caller code: $user->profile (singular) vs $user->posts (collection). Use $user->posts()->first() only if you need a query-builder layer first.
SQLSTATE[23000]: Foreign key constraint failsError signature: integrity constraint violation when creating a model via factory in test.
Root cause: factory chain missing required parent. E.g., a Topic requires forum_id + user_id foreign keys; calling Topic::factory()->create() without specifying parents auto-creates them, but if your factory has explicit for(...) requirements, you need to provide them.
Fix:
Topic::factory()
->for(Forum::factory()->create())
->for(User::factory()->create(), 'author')
->create();
Or use ->hasParent() / ->hasChildren() patterns if the factory defines them.
Error signature: "My computed value isn't updating when the underlying ref changes." OR a reactive value silently becomes a plain primitive after destructuring.
Root cause (a) — reactive destructure loses reactivity:
// ❌ WRONG — count is a plain number, not reactive; changes to state.count don't propagate
const state = reactive({ count: 0 });
const { count } = state;
Fix: use toRefs to preserve reactivity:
import { reactive, toRefs } from 'vue';
const state = reactive({ count: 0 });
const { count } = toRefs(state); // count is a Ref — still reactive
Root cause (b) — refs in arrays don't auto-unwrap:
// ❌ arr[0] is a Ref object, not the number 1
const arr = [ref(1)];
console.log(arr[0]); // Ref<1>
console.log(arr[0].value); // 1 ← correct
Refs only auto-unwrap when accessed as properties of a reactive() object.
Root cause (c) — reactive() on Maps/Sets is opaque:
// ❌ Map mutations don't trigger reactivity
const map = reactive(new Map());
map.set('key', 'value'); // NOT tracked reliably
Fix: use shallowReactive + explicit reassignment, or convert to a plain object/array:
import { ref } from 'vue';
const map = ref(new Map()); // ref triggers update on .value reassignment
map.value = new Map(map.value).set('key', 'value');
Invoke laravel-vue3-specialist agent for deep Vue 3 reactivity audit.
Error signature: view renders the literal messages.button.save instead of "Save".
Root cause: missing lang key in the active locale's translation file. Laravel falls back to the key string when no translation exists.
Fix:
php -r 'require "vendor/autoload.php"; $app = require "bootstrap/app.php"; $app->setLocale("de"); echo (Lang::hasForLocale("messages.button.save", "de") ? "yes" : "no");'
If no, add the key to lang/de/messages.php (or wherever your translation files live). Verify the locale is set correctly in the test (App::setLocale('de') in test setup).
Error signature: assertVisible('@my-button') fails intermittently; CI flake; the element IS in the DOM when you check manually.
Root cause: the element appears asynchronously (Livewire morph, Alpine init, AJAX response), but the assertion ran before it materialized. Manual ->wait(1) is a smell — Pest 4 already has a 5-second implicit timeout on assertVisible/assertPresent/assertSee/assertText/assertAttribute.
Fix:
->wait(N) calls@data-testid over text or class selectors)setUp(fn () => $this->setTimeoutMultiplier(2)) in the test — but flag this for redesign (5s should be enough)Sibling-canon: see laravel-tdd skill §"wait(N) is a smell" for the full pattern.
For deep stack-specific debugging:
| If RED is in... | Dispatch |
|---|---|
| Inertia controller / Vue page | laravel-inertia-specialist — verifies Inertia v3 API surface in node_modules/@inertiajs/vue3/dist/ |
| Vue 3 component / Composition API | laravel-vue3-specialist — audits ref/reactive/computed usage, lifecycle cleanup, composable design |
| Reka UI primitives | laravel-reka-ui-specialist — reads node_modules/reka-ui/dist/ for canonical composition + ARIA contract |
| Pest test (API misuse) | laravel-pest-specialist — reflects on Pest\Expectation + browser plugin |
| Eloquent / architecture | laravel-architect — sibling-canon check against app/Actions/, app/Services/ |
| Multi-component review | laravel-reviewer — composes specialist invocations + banned-token sweep |
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub altraweb/laravel-marketplace --plugin laravel-vue-superpowers