Common Magento 2 Errors and How to Fix Them
Some Magento errors come back on almost every project. They have short, vague messages and a search returns a dozen forum threads, each suggesting a different command to run. Running random commands sometimes makes the error go away without anyone understanding why, and then it comes back.
This article goes through six of the most common ones in Magento 2.4. For each one: what the message actually means, the usual causes, and the fix. Before any of them, two general habits help: read var/log/exception.log and var/log/system.log for the full stack trace, and know which mode the store runs in with bin/magento deploy:mode:show.
"Area code is not set"
What it means
Magento loads configuration by area: frontend, adminhtml, crontab, webapi_rest, graphql and others. Web requests set the area automatically. CLI commands and standalone scripts do not. When code that needs area specific configuration runs before an area is set, you get Magento\Framework\Exception\LocalizedException: Area code is not set.
Common causes
- A custom console command that saves products, sends emails or renders templates.
- A console command whose constructor pulls in heavy dependencies. Every command is instantiated when
bin/magentostarts, so a constructor that triggers area dependent code breaks all commands, even unrelated ones likecache:flush. - A standalone PHP script bootstrapped outside the normal application.
Fix
Set the area inside execute(), never in the constructor:
protected function execute(InputInterface $input, OutputInterface $output): int
{
try {
$this->appState->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML);
} catch (\Magento\Framework\Exception\LocalizedException $e) {
}
$this->exporter->run();
return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
}
Here appState is an injected Magento\Framework\App\State, and the empty catch covers the case where the area was already set. When only one piece of code needs a specific area, emulateAreaCode() on the same class runs a callback in that area and restores the previous state.
For heavy constructor dependencies, inject a proxy through di.xml so the real object is only created when the command runs:
<type name="Vendor\Module\Console\Command\ExportProducts">
<arguments>
<argument name="productRepository" xsi:type="object">Magento\Catalog\Api\ProductRepositoryInterface\Proxy</argument>
</arguments>
</type>
"Class ... does not exist"
What it means
The object manager was asked for a class the autoloader cannot find. Often the class name ends in Factory, Proxy, Interceptor or Extension. Those are generated classes, written to the generated folder either on demand in developer mode or by setup:di:compile in production mode.
Common causes
- Production mode with stale or incomplete generated code, for example after a deploy that skipped
setup:di:compileor after enabling a module. - A typo in a class name in
di.xml, a layout file or a constructor type hint. - The module that provides the class is disabled, or its namespace in
composer.jsondoes not match its folder. - Files in
generatedowned by another user, so Magento cannot write the new class in developer mode.
Fix
rm -rf generated/code/* generated/metadata/*
composer dump-autoload --optimize
bin/magento setup:di:compile
bin/magento cache:flush
In developer mode you can skip the compile step, since classes are generated on the next request. If the error persists after a clean compile, the problem is in the code, not the cache. The compile output usually names the file with the wrong reference. Run bin/magento module:status Vendor_Module to confirm the module is enabled.
"Invalid Form Key. Please refresh the page."
What it means
Every POST in Magento carries a form_key that must match the one stored in the session. The message means they did not match. Either the form sent the wrong key, sent none, or the session that holds the key was lost.
Common causes
- Cookie problems. A cookie domain or path in the configuration that does not match the URL, mixing HTTP and HTTPS, or a browser blocking the session cookie. Check
web/cookie/cookie_domainandweb/cookie/cookie_pathincore_config_data. - Sessions evicted. Redis used for sessions with a
maxmemorypolicy that evicts keys, or a shared instance with the cache that fills up. Sessions disappear and the next POST fails. - Custom forms. A custom template that builds a form without the key. Render the
formkeyblock inside the form withgetBlockHtml('formkey'), or post through JavaScript that sends theform_keyfrom the cookie. - Cached form key. A custom block that prints the form key directly into HTML and is cached by the full page cache, so every visitor gets the same key. Core handles this by updating the key with JavaScript on cached pages. Custom code must not bypass it.
- Large admin forms. A product or configuration page with many fields can exceed PHP
max_input_vars. PHP silently drops the extra fields, and if the form key is among them, the save fails with this message. Raisemax_input_varsin the PHP-FPM configuration.
Fix
Find which one applies before changing anything. Open the browser developer tools, look at the POST payload for form_key and compare it with the form_key cookie. If they differ, it is a caching or template problem. If they match but the error persists, the session is the problem. Check the session storage and the cookie settings.
Indexers stuck
What it means
bin/magento indexer:status shows an indexer as "Processing" for hours, or "Reindex required" that never goes away, and the storefront shows old prices, missing products or wrong stock.
Common causes
- A reindex process was killed, by a timeout, a memory limit or a deploy, and its state was never reset.
- Indexers in "Update by Schedule" mode depend on cron jobs
indexer_update_all_viewsandindexer_reindex_all_invalid. If cron is not running, the changelog tables ending in_clgrow and nothing is applied. - Two reindex processes started at the same time by cron and by someone on the command line.
Fix
bin/magento indexer:status
bin/magento indexer:reset catalog_product_price
bin/magento indexer:reindex catalog_product_price
indexer:reset marks the indexer as invalid so a full reindex is allowed to run again. Then check cron: the cron_schedule table shows whether the indexer jobs ran recently and whether they finished with an error. For production stores, keep indexers on "Update by Schedule" with bin/magento indexer:set-mode schedule and make sure cron runs every minute. Full reindexes on a large catalog should be planned, not triggered from the admin during business hours.
"Unable to unserialize value"
What it means
This comes from Magento\Framework\Serialize\Serializer\Json::unserialize(), usually with "Error: Syntax error". Magento expected a JSON string and got something else.
Common causes
- Old data in PHP serialized format. Before 2.2, many values were stored with PHP
serialize(). A migration or a third party module that still writes that format leaves values starting witha:where JSON is expected. - Configuration fields with a serialized backend model, such as dynamic rows in system configuration, edited by hand in the database or truncated by a column length.
- Corrupt cache entries, for example after a Redis eviction in the middle of a write or a cache shared between two installations.
- An empty string or
nullpassed to the serializer by custom code.
Fix
Flush the cache first. If the error disappears, it was the cache. If not, find the value. The stack trace shows which class called the serializer, which tells you whether it is configuration, a quote item option, an order item, or a module table. For configuration, look for values that are not valid JSON:
SELECT config_id, path, LEFT(value, 60)
FROM core_config_data
WHERE value LIKE 'a:%' OR value LIKE 's:%';
Convert those values to JSON with a small script that unserializes and encodes them, or save the field again in the admin. Fix the module that writes the old format, otherwise the error returns.
Static files return 404
What it means
The page loads without styles, and the browser console shows 404 for files under /static/version.../frontend/....
Common causes
- Production mode without deployed static content, or deployed for the wrong locale or theme.
- The web server rule that strips the version segment from static URLs is missing. Magento's sample Nginx configuration and
pub/static/.htaccessinclude it. - The document root points to the Magento root instead of
pub, so URLs and rewrite rules do not match. - Files owned by the wrong user, or
pub/staticemptied by a deploy script.
Fix
bin/magento setup:static-content:deploy -f en_US pt_BR
bin/magento cache:flush
Deploy every locale used by a store view and by admin users. For Nginx, compare your static location block with nginx.conf.sample in the Magento root. It contains the rewrite from /static/version123/ to /static/ and the fallback to static.php for files not yet generated in developer mode.
A general rule
Most of these errors are symptoms of something in the environment: cron, sessions, deploy steps, file ownership. Fix the cause and write it into the deploy script or the server configuration, so the next person does not search for the same message again.
FAQ
How do I fix "Area code is not set" in a Magento 2 console command?
Inject Magento\Framework\App\State and call setAreaCode inside the execute method, wrapped in a try and catch. Do not set it in the constructor, and use proxies for heavy dependencies so bin/magento does not load them at start.
Why does Magento say Class Factory does not exist in production mode?
Factories are generated classes. In production mode they must be created by bin/magento setup:di:compile. Clear generated/code and generated/metadata, run the compile, and flush the cache.
What causes Invalid Form Key when saving a product in the admin?
Often a large form exceeding PHP max_input_vars, which drops fields including the form key. Lost sessions and wrong cookie domain settings are the other usual causes.
Is it safe to run indexer:reset?
Yes. It only marks the indexer as invalid so it can be rebuilt. Run indexer:reindex right after it, and avoid doing a full reindex of a large catalog during peak traffic.
I am Rodrigo Biazzi, a Magento Certified Developer working with Magento and Adobe Commerce for more than 12 years. I am available for freelance Magento work, from single fixes to longer projects. If this is the kind of problem you have on your store, get in touch.
Related articles
Magento 2 GraphQL: Add a Custom Query and Resolver
How to add a custom GraphQL query in Magento 2.4: schema.graphqls, a resolver with ResolverInterface, errors, auth checks and cache identity for Varnish.
Magento 2 Admin Security Hardening Checklist
Harden the Magento 2 admin: custom admin path, IP restriction, 2FA, lockout and session settings, user and role cleanup, API integrations and admin logs.
Magento 2 Admin Two Factor Authentication: Setup and Fixes
Set up Magento 2 admin two factor authentication: providers, forcing a provider, CLI commands to reset users, local development, API tokens and common problems.
Magento 2 Backup and Restore: What to Save and How
How to back up and restore a Magento 2 store: database dumps that import cleanly, media, env.php and the encryption key, offsite copies and tested restores.