19 Laravel Bug Fixes — Tested, Verified, Ready to Merge
Update (2026-03-19): This has grown to 91 PRs across 27 repos. See the Day 2 update for the full list with confidence ratings.
I found and fixed 19 bugs across the Laravel ecosystem — 10 Windows-specific and 9 cross-platform. Every fix has been tested on Windows 11 Pro and independently verified on macOS (Darwin 25.3.0, PHP 8.4.19) with bug reproduction, concurrency testing, and full test suite runs.
I'm currently blocked from the Laravel GitHub organisation, so I can't submit PRs or comment on issues.
All patches and PRs are linked below and in the gist — feel free to use them directly. I can't push these upstream because submitting too many at once gets you blocked, and the contribution guide states that AI-generated pull requests will be closed without review. These are 100% AI-generated using Claude Code — every fix was found, written, and tested by AI on my machine.
Full details and patches: GitHub Gist
The Fixes
Cross-Platform (Production Critical)
laravel/cashier-stripe #1759 — Webhook race condition creates duplicate subscriptions. When customer.subscription.created and customer.subscription.updated webhooks arrive simultaneously, both handlers insert a new row. Active data corruption in production. Fix catches UniqueConstraintViolationException and recovers gracefully. Verified with real concurrent processes on both SQLite and PostgreSQL — race triggered 10/10 times, fix recovered every time. Patch
laravel/framework #58207 — OOM jobs retry infinitely. When a job causes an out-of-memory kill, the exception counter never increments because the catch block never executes. Jobs with $maxExceptions retry forever. 31 comments. Fix uses optimistic increment — counter goes up before fire(), down on success. Verified with real OOM kills (PHP exit code 255). Patch
laravel/framework #56395 — Pipeline memory leak. Pipeline::then() retains references to job objects after completion, preventing garbage collection. Workers hold 254MB instead of 4MB after processing 50 jobs. Two-line fix. 22 comments. Patch
laravel/reverb #344 — TypeError crash. Operator precedence bug in isWebSocketRequest() means any non-null Upgrade header is treated as a websocket request. Plus no guard when reverse proxies strip headers. 17 comments, users switching to Soketi. All 126 tests pass including Redis. Patch
laravel/serializable-closure #126 — v2.0.9 regression breaks Bus::chain. Objects with __serialize skip closure wrapping entirely, causing serialization failures. Fix scopes the skip to only Closure-typed properties. 338 tests pass. Patch
laravel/cashier-stripe #1817 — swapAndInvoice gives free upgrades. When payment fails during a plan swap, users end up on the expensive plan for free. Fix adds pendingIfPaymentFails() as the default. Patch
laravel/horizon #1535 — Silent 60-second queue stalls. Memory-exceeded workers restart, die again, then cooldown for 60 seconds with zero logging. Impossible to diagnose. Fix adds logging at 3 critical points. 169 tests pass. Patch
laravel/pulse #461 — Multi-server deadlocks. Concurrent pulse:work processes read the same Redis stream entries and deadlock on MySQL upserts. Fix adds atomic Redis lock around digest. Patch
laravel/framework #57070 — Sub-minute scheduling skips. endOfMinute() mutates the Carbon instance used in the loop condition. Fix uses copy()->endOfMinute(). 68 scheduling tests pass. Patch
Windows-Specific
laravel/octane #1100 — FrankenPHP crashes on Windows. POSIX signal constants don't exist. PR
laravel/wayfinder #128 — Backslash import paths. DIRECTORY_SEPARATOR produces \ in TypeScript. Patch
laravel/sail #843 — Testing database never created. Bash shebang fails in MySQL containers. PR
laravel/sail #850 — Installation fails on Windows. Unrecognised OS. PR
laravel/installer #472 — laravel new crashes with Boost. No TTY support. Patch
laravel/vs-code-extension #575 — Symlinked packages break. Double path construction. Patch
laravel/prompts #191 — Terminal rendering glitches. PHP_EOL newline miscounting. Patch
laravel/octane #1034 — File watcher ignores --poll. FrankenPHP overrides parent watcher. PR
laravel/sail #815 — Headed browser tests fail. No virtual display. PR
laravel/vite-plugin-wayfinder #10 — Wayfinder fails in Vite. Working directory resolution. Patch
Verification
Every fix was tested on Windows 11 Pro 26200 and macOS Darwin 25.3.0 (PHP 8.4.19). Cross-platform fixes include:
- Bug reproduction with before/after proof
- Full test suite runs (2,500+ tests total across all repos)
- Concurrency testing with
pcntl_forkand barrier synchronisation - Real OOM kills (PHP memory limit exhaustion, exit code 255)
- Memory leak measurement (WeakReference + memory profiling)
- PostgreSQL and Redis integration testing via Docker
All patches, verification data, and test results are in the gist.
EDIT (2026-03-19): The count has grown from 19 to 25 fixes across 11 Laravel repos. Here's what was added:
New Cross-Platform Fixes
laravel/framework #57262 — incrementEach() updates ALL rows in the table. DATA CORRUPTION — calling $model->incrementEach() on an Eloquent model updates every row, not just that model. Multiple reports of near-production data loss. Fix adds explicit incrementEach()/decrementEach() methods to Eloquent Builder that call toBase() to apply scopes. All 195 builder tests pass. Patch
laravel/framework #58377 — SessionManager creates duplicate Redis connections. Every request with Redis sessions opens 2 connections instead of 1 due to an unnecessary clone. Causes RedisException: Connection limit reached in strict environments. 12 comments. One-line fix — remove the clone. All 93 session tests pass. Patch
laravel/reverb #273 — Presence channels return wrong user list when scaling with Redis. Each Reverb instance maintains its own local member list. Presence events never propagated via Redis pub/sub to other instances. Completely breaks presence channels at scale. Fix routes member events through the Redis event dispatcher and adds a new PRESENCE_DATA metric type for cross-instance member gathering. 237 tests pass. Patch
laravel/octane #1004 — No zero-downtime deployment. octane:reload doesn't work with symlink-based deployments (Deployer, Envoyer). 23 comments, 5-30s downtime per deploy. New ResolvesSymlinks trait detects symlinked directories and ensures all three server types (Swoole, RoadRunner, FrankenPHP) use the symlink path. clearstatcache(true) on reload. 180 tests pass. Patch
laravel/scout #957 — Timeout jobs block queue forever. Scout's MakeSearchable jobs don't set failOnTimeout, so timed-out jobs silently hang in Horizon. One-line fix. All 211 Scout tests pass. Patch
New Windows Fixes
laravel/wayfinder #178, #161, #159 — Three TypeScript generation bugs. #178: Generated .d.ts breaks Inertia type merging (missing import). #161: Dashed route names produce invalid TS identifiers. #159: Route action named "options" shadows the routeOptions parameter. Patch
Updated Fixes
Cashier #1817 (swapAndInvoice) was redesigned. The original pendingIfPaymentFails() approach was incompatible with Stripe's API (rejects tax_rates with pending_if_incomplete — verified with real Stripe test key, 8 API errors). The new fix uses webhook reconciliation: a handleInvoicePaymentFailed() handler detects failed subscription update payments and syncs the local DB back to Stripe's actual state. All 44 feature tests pass with real Stripe API key. PR
Cashier #1759 (duplicate subscriptions) now also has a PR on my fork with the race condition fix verified against real Stripe API — all 44 tests pass. PR
Current Totals
- 25 fixes across 11 repos (was 19)
- 5 PRs on forks: sail (3), octane (2), cashier-stripe (2)
- 18 gists with patches for repos I can't fork
- All fixes in the master gist