From laravel-api-tool-kit
Investigates and debugs Laravel API issues like wrong responses, missing data, queues, auth, performance using data-first workflow checking DB, cache, configs before code.
npx claudepluginhub ahmedesa/laravel-api-tool-kitlaravel-api-tool-kit/workflows/# Workflow: Investigate & Debug Structured debugging workflow for any issue — wrong response, missing data, queue failures, auth problems, performance issues. Works for any Laravel API project. **Trigger phrases**: "investigate", "debug", "something is wrong with", "why is X not working", "track down", `/investigate` --- ## Core Principle: Data Before Code — ALWAYS **NEVER start by reading code.** Code is complex and usually correct. Most issues are configuration or data problems. Before touching any PHP file, query the database and check application state. Only read code if the data ...
/debugDebugs issues by investigating logs, SQLite database state, git history, and service status, reporting key errors, warnings, and discrepancies.
/debugPerforms systematic debugging using ReAct pattern: clarifies problem with user, forms ranked hypotheses, investigates root cause via evidence-based cycles before fixes.
/debugging-promptsGenerates effective Laravel debugging prompts with complete error information and context for AI-assisted troubleshooting.
/vt-debugInvestigates bugs by reproducing issues, tracing code, identifying root causes, and documenting findings. Accepts bug IDs, paths, or symptoms; supports --deep and --fresh flags.
/debugInvestigates errors in files or production issues using Logfire traces: finds recent exceptions, runs SQL queries on OpenTelemetry data, suggests code fixes, shares trace links.
/investigateInvestigates bugs via structured root cause analysis: scopes to module, collects evidence, forms hypotheses, implements evidence-based fixes.
Share bugs, ideas, or general feedback.
Structured debugging workflow for any issue — wrong response, missing data, queue failures, auth problems, performance issues. Works for any Laravel API project.
Trigger phrases: "investigate", "debug", "something is wrong with", "why is X not working", "track down", /investigate
NEVER start by reading code. Code is complex and usually correct. Most issues are configuration or data problems.
Before touching any PHP file, query the database and check application state. Only read code if the data looks correct but the outcome is still wrong.
This rule exists because tracing code paths feels productive but wastes hours. A 30-second query eliminates a 2-hour code trace.
Recognizing the pattern from the symptom lets you skip straight to the right data source. Match the symptom before opening any file:
| Pattern | Symptom | First Check |
|---|---|---|
| Cache | Data correct in DB but API returns stale/wrong value | Compare Cache::get('KEY') vs DB value |
| Queue / async | Action should have happened but didn't (email, notification, counter) | failed_jobs table, Horizon status, listener exceptions |
| Timezone | Wrong dates, records missing from date ranges, off-by-one on time queries | UTC in DB vs local time in request |
| Field / type mismatch | Query returns nothing unexpectedly, comparison always fails | information_schema.columns — check actual column type and name |
| Business logic | Feature worked before, stopped after deploy or data change | Read the requirement vs the actual code path |
| Config / env | Works locally, broken on staging/production | config('key') via tinker — env vars may not be loaded after config:cache |
The right pattern for your project depends on its architecture. If it uses heavy caching, cache bugs dominate. If it's queue-heavy, async issues dominate. Use the knowledge base (Step 0) to see what this project's common patterns actually are.
Before investigating anything, check if this is a known issue:
.claude/knowledge/ (Claude Code), .agent/knowledge/ (Antigravity), or knowledge/ (project root for Cursor/Copilot)If match: Tell user what we know → verify with data → propose fix. If no match: "No prior knowledge for this area. Investigating fresh."
Get from the user:
Do NOT ask for error messages, logs, or curl responses before starting — just start investigating with whatever you have.
Check the data from three sources and compare them. Mismatches between sources reveal the bug:
How to query: Use the best available method, in this priority order:
mcp__postgres__query, mcp__mysql__query, or any database MCP server are available, use them directly. They give instant SQL access without needing a shell or tinker session.php artisan tinker — Run Eloquent or raw DB queries via the shell.Tip: Check available tools at the start of investigation. If you see any MCP tool with "postgres", "mysql", "database", "db", or "sql" in the name — use it for all database queries throughout the investigation.
-- What does this record actually look like?
SELECT * FROM orders WHERE id = 'ORDER_ID';
-- Check column types (catches VARCHAR-vs-INT gotchas)
SELECT column_name, data_type FROM information_schema.columns
WHERE table_name = 'TABLE_NAME';
-- Recent records
SELECT * FROM orders WHERE user_id = 'USER_ID' ORDER BY created_at DESC LIMIT 10;
-- Count affected records
SELECT status, COUNT(*) FROM orders
WHERE created_at > NOW() - INTERVAL '24 hours' GROUP BY status;
-- Missing relationships
SELECT o.id, o.user_id, u.id as user_exists
FROM orders o LEFT JOIN users u ON u.id = o.user_id
WHERE o.id = 'ORDER_ID';
# Test the endpoint directly
curl -s -X GET "http://localhost/api/orders/ORDER_ID" \
-H "Authorization: Bearer TOKEN" \
-H "Accept: application/json" | jq .
Compare API response with DB data. If they differ → cache issue, resource transformation bug, or middleware problem.
For raw model state, casts, accessors, and relationships — use tinker. If MCP database tools are available, you can also run raw SQL here to cross-check Eloquent behavior vs raw DB values (this comparison itself catches cast/accessor bugs).
# Check model state (includes casts, accessors, relationships)
php artisan tinker --execute="echo json_encode(App\Models\Order::find('ORDER_ID')->toArray(), JSON_PRETTY_PRINT);"
# Check cache
php artisan tinker --execute="echo Cache::get('CACHE_KEY');"
# Check config
php artisan tinker --execute="echo json_encode(config('services.stripe'), JSON_PRETTY_PRINT);"
# Test a service method directly
php artisan tinker --execute="echo app(App\Services\OrderService::class)->calculateTotal('ORDER_ID');"
| DB vs API | DB vs Tinker | Diagnosis |
|---|---|---|
| DB correct, API wrong | DB correct, Tinker correct | Resource transformation or cache issue |
| DB correct, API wrong | DB correct, Tinker wrong | Model accessor/cast or scope problem |
| DB wrong | - | Data was written incorrectly — trace the write path |
| DB correct, API correct | - | Issue is elsewhere (frontend, auth, timing) |
If data looks correct across all sources, check the application layer:
# Recent errors
tail -n 200 storage/logs/laravel.log | grep -i "error\|exception\|failed"
# Feature-specific
grep -i "order\|payment\|FEATURE" storage/logs/laravel.log | tail -50
Use MCP database tools for these queries if available — they're faster than tinker for SQL:
-- Stuck jobs
SELECT id, queue, payload->>'displayName' as job, attempts, created_at
FROM jobs ORDER BY created_at DESC LIMIT 20;
-- Failed jobs
SELECT id, queue, payload->>'displayName' as job, exception, failed_at
FROM failed_jobs ORDER BY failed_at DESC LIMIT 10;
# Horizon status
php artisan horizon:status
php artisan queue:failed
# Is stale cache the problem?
php artisan tinker --execute="
\$cached = Cache::get('KEY');
\$fresh = Model::find('ID');
echo 'Cached: ' . json_encode(\$cached) . PHP_EOL;
echo 'Fresh: ' . json_encode(\$fresh) . PHP_EOL;
"
STOP. Ask yourself:
Only open PHP files if:
When reading code, focus on:
Common Laravel logic traps:
auth()->id() in a queued job (always null in queue)$request->all() instead of $request->validated()whenLoaded() causing silent N+1DB::transaction() with queued listener->get() loading entire table instead of pagination->format() returns server timezone, not user timezoneCacheKeys class)NEVER declare root cause without data proof. Show:
If you can't prove it with a query or test → you haven't found it yet.
Before presenting, spend 5 minutes attacking your conclusion:
After confirming root cause:
.claude/knowledge/[feature].md, .agent/knowledge/[feature].md, or knowledge/[feature].md)One file per feature — never one file per ticket.
Structure:
After finishing, ask: "Will someone hit this type of issue again?"
If yes — run /create-workflow to capture this investigation as a specialized workflow (e.g., workflows/cache-investigator.md, workflows/queue-debugger.md).
This is how server-investigator.md and firebase-investigator.md were born — from real investigations that turned into reusable workflows.
❌ Reading code depth-first before querying data ❌ Spending 30+ min on one hypothesis without testing it ❌ Asking for logs, curl responses, ticket numbers before starting — just start ❌ Reading 5+ files trying to "understand the flow" ❌ Declaring root cause without data proof ❌ Assuming code bug when config is more likely ❌ Creating standalone investigation docs — use knowledge files ❌ Finishing an investigation without updating knowledge
✅ Query data first, every time ✅ Compare DB vs API vs Tinker — mismatches reveal the bug ✅ State hypothesis in one sentence, test with one query ✅ Checkpoint at 15 minutes ✅ Prove root cause with numbers ✅ Challenge findings before presenting ✅ Update knowledge so the next investigation starts ahead ✅ Create a specialized workflow if the pattern will recur