Laravel Laravel Development

    Upgrading Laravel 10 to 12: What Actually Breaks and How to Fix It

    December 25, 2025
    Updated: September 14, 2026
    Larastaff
    13 min read
    Upgrading Laravel 10 to 12: What Actually Breaks and How to Fix It

    Why Laravel Upgrades Matter

    Real upgrades become painful because, one, they are usually time-consuming, and two, they just get forgotten about.

    You can keep pushing Laravel upgrades off forever, but the further behind you get, the more CVEs can come out. A lot of times, these upgrades are not just about getting new features. They are specifically protecting you from critical vulnerability issues that have been covered in newer releases.

    That is the main reason to stay on top of releases. Once an old vulnerability is discovered, attackers are actively looking for applications that are still exposed to it.

    Aside from that, there are a lot of quality-of-life improvements that come with each release. Developers actively watch for this stuff. I constantly have developers on my team sending me messages like, “Hey, look at this new upgrade,” or “Look at the latest thing they released in this PHP version.”

    So there are a lot of different reasons you might want to upgrade, but practically, the most important one is patching against CVEs. People know what is vulnerable in those older versions, so you need to stay on top of it.

    The good news is that staying current is more practical than it used to be. Manual upgrades, AI-assisted upgrades, and tools like Laravel Shift have made it easier than ever to keep moving forward instead of letting upgrades sit forever.

    Upgrade One Version at a Time

    You can upgrade directly from Laravel 10 to 12 in one step. But for most apps, we recommend going through Laravel 11 first.

    Correct upgrade order:

    1. Laravel 10 → Laravel 11
    2. Laravel 11 → Laravel 12

    The standard advice is always to go up one version at a time.

    Part of this is for testability, but the main thing is that between each version, we can see exactly what changes need to be made from the last version. If you go straight from Laravel 10 to Laravel 12, you may miss the upgrades that needed to happen from 10 to 11 before you ever got to 11 to 12.

    So it is practical advice. Every time you do this, use a step-up method. You kind of graduate from one version to the next.

    You do Laravel 10 to Laravel 11 first. Then you do actual QA testing on the entire site and make sure nothing broke between 10 and 11 before jumping to 11 to 12.

    That way, if something does break, you know what you are looking at. It makes you or your developer have a much better time solving it.

    If you jump from 10 to 12 and something breaks, your developer may be looking for an issue from the 10 to 11 upgrade and not know it. That makes the issue harder to find and harder to fix.

    Should You Go to Laravel 13 Instead?

    Laravel 13 was released in March 2026, so it’s a fair question.

    Our advice is to upgrade to 12 first, then decide. Everything in this guide is still required either way, because you can’t reach 13 without the changes from 11 and 12. Once you’re stable on 12, moving to 13 is a small step. Laravel’s own guide estimates about ten minutes for the framework changes.

    The main thing to check is PHP. Laravel 13 needs PHP 8.3 or newer, while Laravel 12 runs on 8.2. If your servers already run PHP 8.3 and your packages support Laravel 13, you can continue straight on once 12 is stable. If not, stop at 12 and plan the PHP upgrade separately.

    Step 1: Prepare Before Upgrading

    Most upgrade problems can be found before you change a single line. Spend an hour here and you’ll save a day later.

    Upgrade PHP first. Laravel 11 and 12 need PHP 8.2 or newer. Laravel 10 already supports PHP 8.2 and 8.3, so move PHP while you’re still on Laravel 10, deploy it, and make sure everything works. That way, a PHP problem never gets mixed up with a Laravel problem.

    Find packages that will block you. Run these two commands:

    composer outdated --direct

    composer why-not laravel/framework 11.0

    The second command lists every package that isn’t ready for Laravel 11. Each one needs to be updated, replaced, or removed before you continue. If Composer marks a package as abandoned, deal with it now. Those are the ones that stall upgrades.

    Make sure you have a safe starting point:

    • Commit all pending changes and tag the current release
    • Take a database backup and confirm you can restore it
    • Run your tests and make sure they pass
    • Write down your queue jobs and scheduled commands so you can check them later

    If you don’t have many tests, don’t try to write hundreds now. Write a few tests for the things that cost money when they break: login, checkout, payments, and your main API endpoints.

    Step 2: Upgrade Laravel 10 to Laravel 11

    Update your composer.json:

    "require": {
        "php": "^8.2",
        "laravel/framework": "^11.0",
        "laravel/sanctum": "^4.0"
    },
    "require-dev": {
        "nunomaduro/collision": "^8.1"
    }

    Then run:

    composer update --with-all-dependencies

    If you use other official Laravel packages, update them too. For example, Passport goes to ^12.0, Telescope to ^5.0, Cashier to ^15.0, and Livewire to ^3.4.

    What breaks in Laravel 11

    1. PHP Version Compatibility Issues

    Symptoms

    Composer refuses to install and shows something like this:

    laravel/framework[v11.0.0, …, v11.x] require php ^8.2

    -> your php version (8.1.27) does not satisfy that requirement.

    Fix

    • Upgrade PHP on your local machine, your CI pipeline, and every server
    • Check Docker images and hosting settings too, not just your laptop
    • Keep all environments on the same PHP version, so your tests match production

    2. Sanctum, Passport, and Telescope Migration Errors

    In Laravel 11, these packages stopped loading their database migrations automatically.

    Symptoms

    If you previously disabled their migrations, the app crashes right after the upgrade:

    Call to undefined method Laravel\Sanctum\Sanctum::ignoreMigrations()

    Fix

    • Remove Sanctum::ignoreMigrations() from your AppServiceProvider.
    • Check database/migrations first. Most Laravel 10 apps already contain Sanctum’s create_personal_access_tokens_table migration. If yours does, you don’t need to publish anything.
    • For Passport and Telescope, publish the migrations:

    php artisan vendor:publish --tag=passport-migrations

    php artisan vendor:publish --tag=telescope-migrations

    • Then compare the published filenames against the rows in your migrations table. Your production database already has these tables. If the filenames don’t match the existing entries, rename the files to match, so migrate treats them as already run.

    3. Middleware and Authentication Issues

    This one is usually self-inflicted. New Laravel 11 projects don’t have an app/Http/Kernel.php file, and middleware is registered in bootstrap/app.php instead. Developers see this in tutorials and try to move their existing app to the new structure during the upgrade.

    Symptoms

    • Middleware stops running after Kernel.php is deleted
    • Auth guards or route middleware stop working
    • Pages load that should be protected

    Fix

    This comes down to order of operations.

    If the main thing is to get the upgrade done, then get the upgrade done first. I would not necessarily say to avoid restructuring or cleanup in general. You can do that immediately after.

    It is nicer to move off the older structure in general, because developers who have only worked with newer versions of Laravel may not know where things are in an older app layout. So I actually would suggest making that change.

    You just may not need to make it initially.

    My philosophy would be: upgrade the framework first, stabilize the application, make sure middleware, auth, routes, queues, and the main flows still work, and then do the structure cleanup as the next step.

    Do not combine the Laravel upgrade and the app structure modernization into one confusing change unless you have a very specific reason to do that.

    4. Database Columns Losing Their Settings

    This one doesn’t show an error, which makes it easy to miss.

    Symptoms

    A migration that changes a column also removes its default value, unsigned, or comment.
    In Laravel 11, when you use ->change(), you have to repeat every setting you want to keep. Anything you leave out is removed.

    // Original column
    $table->integer('credits')->unsigned()->default(0);
    
    // In Laravel 11, this removes "unsigned" and the default
    $table->integer('credits')->nullable()->change();
    
    // Repeat everything you want to keep
    $table->integer('credits')->unsigned()->default(0)->nullable()->change();

    Fix

    • Repeat all column settings when you write new change() migrations
    • If you have many old change() migrations, squash them with php artisan schema:dump, so they aren’t re-run on fresh databases in CI

    Step 3: Upgrade Laravel 11 to Laravel 12

    Laravel 12 is a small release. Laravel’s own guide estimates about five minutes for the framework changes, and for most apps that’s fairly accurate. Update your composer.json:

    "laravel/framework": "^12.0",
    "phpunit/phpunit": "^11.0"

    If you use Pest, update it to ^3.0 instead of PHPUnit. Then run:

    composer update --with-all-dependencies

    The framework is rarely the problem at this step. The problems come from packages, dates, and tests.


    What breaks in Laravel 12

    1. Composer Dependency Conflicts

    Problem

    Some third-party packages don’t support Laravel 12 yet, and Composer stops:

    Your requirements could not be resolved to an installable set of packages.

      Problem 1

        – Root composer.json requires laravel/framework ^12.0

        – vendor/billing-sdk v2.4.0 requires illuminate/support ^9.0|^10.0

    Fix

    Find out exactly which packages are blocking you:

    composer why-not laravel/framework 12.0

    Then work through them in this order:

    • Update the package to a newer version that supports Laravel 12. Read its changelog first.
    • Replace it with a maintained alternative if it’s no longer maintained.
    • Remove it temporarily if the feature it powers can be switched off.

    Tip: Avoid –ignore-platform-reqs in production. It doesn’t fix the conflict, it just hides it until something breaks at runtime.


    2. Type Errors and Wrong Date Calculations (Carbon 3)

    Laravel 12 requires Carbon 3, the date library Laravel uses everywhere. Carbon 3 is stricter about types, and it changes how date differences are calculated. Composer may already install it during the Laravel 11 step, so you might see these problems earlier.

    Symptoms

    Errors like this, usually from a config value read from .env:

    Carbon\Carbon::rawAddUnit(): Argument #3 ($value) must be of type int|float, string given

    Or tests that suddenly get negative numbers:

    Failed asserting that two strings are equal.

    -‘2’

    +’-2′

    Why it happens

    Values from .env are always strings, and Carbon 3 no longer accepts strings where it expects numbers. Also, diffInDays() and the other diffIn* methods now return decimals, and they return a negative number when the date you compare against is in the past.

    $today   = Carbon::parse('2026-01-10');
    $renewal = Carbon::parse('2026-01-07');
    $today->diffInDays($renewal);
    
    // Carbon 2: 3
    // Carbon 3: -3.0

    Fix

    • Cast config values before passing them to Carbon: now()->addMinutes((int) config(‘session.lifetime’))
    • Where you need the old behavior, pass true and cast the result: (int) $today->diffInDays($renewal, true)
    • Search your code for every date difference and check each one:

    grep -rn "diffIn" app/ resources/ routes/ database/

    Pay the most attention to anything involving money or deadlines: trial periods, billing cycles, due dates, and reports.


    3. Queue, Job, and Event Failures

    Symptoms

    • Jobs fail with confusing errors right after deployment
    • Jobs behave as if the upgrade never happened

    Causes

    Queue workers are long-running processes. They keep the old code in memory until they restart. After an upgrade, a worker running old Laravel code picks up jobs meant for the new code, or the other way around.

    Fix

    • Restart workers on every deployment:

    php artisan queue:restart

    # or, if you use Horizon:

    php artisan horizon:terminate

    • For a major upgrade, let the queue empty out before you deploy, so old jobs aren’t processed by new code
    • Check the failed_jobs table after deploying, and retry or clear jobs as needed

    4. Broken Tests After Upgrade

    Tests may fail even when the application works fine.

    Symptoms

    • A wall of deprecation warnings when you run your tests
    • Date-related tests failing (see Challenge 2)

    Fix

    • Laravel 12 uses PHPUnit 11, which deprecates old-style annotations like /** @test */ and @dataProvider. Replace them with PHP attributes such as #[Test]. Rector can do this for you automatically.
    • Fix date tests using the Carbon fixes above, rather than changing the expected values to match the new results

    5. Uploads and IDs Behaving Differently

    These are smaller changes that only affect some apps, but they’re confusing when you hit them.

    • SVG uploads fail validation. The image rule no longer accepts SVG files by default, because SVGs can contain scripts. If users upload SVG logos, use image:allow_svg, and only where you clean the uploaded files.
    • New UUIDs look different. Models using HasUuids now create ordered UUIDv7 values. Existing IDs don’t change. If another system expects version 4 UUIDs, use the HasVersion4Uuids trait on those models.
    • Files saved to the wrong folder. If your config/filesystems.php doesn’t define a local disk, files now go to storage/app/private instead of storage/app.

    Don’t upgrade a large application in one big change.

    Safe approach:

    • Upgrade on a separate branch, with one pull request per version
    • Run your CI on the same PHP version as production
    • Test on staging with a copy of real data, because migrations behave differently on real schemas than on empty databases
    • Deploy one version, watch it for a few days, then do the next
    • Keep your previous release and database backup ready, in case you need to roll back

    This keeps each change small, easy to review, and easy to undo


    Post-Upgrade Checklist

    After deploying Laravel 12, run:

    php artisan optimize

    php artisan queue:restart

    Then check:

    • Login, logout, and password reset work
    • API authentication works
    • Payments and anything involving dates give the correct results
    • Emails, file uploads, and webhooks work end to end
    • Scheduled commands run on time
    • The failed_jobs table and your error logs stay clean for 24 to 48 hours
    • composer audit reports no known security issues

    Where Upgrades Actually Fail

    • Third-party packages, not Laravel. Abandoned or unpinned packages block the Composer resolution long before any Laravel code runs.
    • Migrations that fail silently. Laravel 11’s native ->change() drops any column attribute you don’t restate. No error, no warning — just a column that quietly lost its default.
    • Dates. Carbon 3 stopped coercing strings to integers, so config-driven date math throws at runtime instead of at deploy.
    • Stale queue workers. Jobs keep executing pre-upgrade code until the workers are restarted, which makes the upgrade look broken hours after it succeeded.

    Final Thoughts

    Upgrading from Laravel 10 to 12 is worth doing, and it’s more manageable than it looks. The framework changes are small. Most of the work is in packages, database migrations, and dates, and all of those can be found and fixed before they reach production.

    Go one version at a time, test each step, and pay extra attention to anything involving money or deadlines.

    Laravel 12 is supported with security fixes until February 24, 2027, so once you’re there, add the next upgrade to your roadmap well before that date.

    Free Laravel upgrade audit — send us your composer.json and we’ll return a package-by-package compatibility report and an effort estimate in 3 business days. Contact US.

    Larastaff

    Larastaff

    Related Articles

    One comment on “Upgrading Laravel 10 to 12: What Actually Breaks and How to Fix It”

    1. Hi there! This is my first visit to your blog! We are a team of volunteers and starting a new initiative in a community in the same niche.
      Your blog provided us beneficial information to work on. You
      have done a wonderful job!

    Leave a Comment

    Your email address will not be published. Required fields are marked *