Your store shows a white page with one line in bold: “There has been an error processing your request”. Below it, a short sentence about security and a long string of letters and numbers. Customers can’t buy, the client is on the phone, and the page tells you nothing about what broke.
This is the most famous error page in Magento 2 and Adobe Commerce. Every developer, every agency and a lot of store owners have seen it. The good news is that the page gives you everything you need to find the real problem. Most people just don’t know where to look.
In this post of the Magento Fix Log, I’ll show what Magento does behind that page, how to turn the record number into the real error in a few minutes, and the settings in pub/errors that most developers never open. Everything here was checked in the code of Magento 2.4.7-p10, 2.4.8-p5 and 2.4.9.
What the page is telling you
This is what a visitor sees:
There has been an error processing your request
Exception printing is disabled by default for security reasons.
Error log record number: 8f3c1d0e9a7b... (64 characters)The page itself is not the problem. It’s what Magento shows when an exception reaches the very top of the application and nobody caught it on the way.
Here’s how it works. In a web request, Magento\Framework\App\Bootstrap::run() catches any \Exception and passes it to Magento\Framework\App\ExceptionHandler::handle(). That method tries five things, in this order:
- Developer mode: print the full exception as plain text and stop.
- Maintenance mode: show the 503 page from
pub/errors/503.php. (If Magento isn’t installed yet, it redirects to setup instead.) - Session exception: redirect the visitor to the home page.
- Init exception: show the 404 page from
pub/errors/404.php. - Anything else: save a report and show the page you’re looking at.
So if you see this page, three things are true. The store is not in developer mode, an exception escaped all the code that could have handled it, and Magento wrote the details to disk. The response also carries HTTP status 500, set in pub/errors/processor.php, so your uptime monitoring should see it too.
One more detail most people miss. Magento registers its own PHP error handler in Bootstrap, and Magento\Framework\App\ErrorHandler turns PHP warnings and notices (the ones enabled in error_reporting) into exceptions. That means a simple Warning: Undefined array key in a custom module can also end on this page.
The quick fix
If the store is down and you need the short version:
# 1. Copy the record number from the page, then search the log
grep -n "PASTE_THE_RECORD_NUMBER" var/log/exception.log
# 2. Find the report file, wherever it is
find var/report -type f -name "PASTE_THE_RECORD_NUMBER"
# 3. Read the error message, the trace and the URL
jq -r '.["0"], .["1"], .url' var/report/PASTE_THE_RECORD_NUMBERThe message tells you the real error. Fix that, and the page goes away. The rest of this post explains each step and the traps around them.
Step 1: Search exception.log for the record number
Start here. It works even when all you have is access to the logs.
When ExceptionHandler builds the report, it logs the exception as critical and adds the record number to the log context:
$reportData['report_id'] = $this->encryptor->getHash(implode('', $reportData));
$this->logger->critical($exception, ['report_id' => $reportData['report_id']]);Magento’s logger (Magento\Framework\Logger\LoggerProxy) puts the exception object in the context, and the system log handler sends every record with an exception to var/log/exception.log. The log record carries the report_id, and its message is the exception converted to text. That’s PHP’s own format: the class, the message, the file and line, the stack trace, and any previous exceptions in the chain.
So the record number on the page is a search key:
grep -n "PASTE_THE_RECORD_NUMBER" var/log/exception.logOpen the file at that line and you have the exception class, the message and the trace.
Here’s a detail that makes this even more useful. The record number is not random. It’s an HMAC-SHA256 of the error message, the trace, the URL and the script name, keyed with your store’s encryption key (EncryptorInterface::getHash() without a salt). In practice:
- The same error on the same URL always gets the same number on that store. Refreshing the page won’t give you a new one.
- A different URL or a different trace gives a different number.
That turns grep into a quick counter:
grep -c "PASTE_THE_RECORD_NUMBER" var/log/exception.logIf the count is one, it was probably a one-off. If it’s in the hundreds, customers are hitting it all day.
Step 2: Open the report in var/report
pub/errors/report.php saves the same data to a file named after the record number. By default the path is:
var/report/PASTE_THE_RECORD_NUMBERThe file is JSON (Magento uses its Json serializer). The keys are a bit unusual, because the data starts as a PHP array with numeric keys:
| Key | Content |
|---|---|
"0" | the exception message |
"1" | the stack trace as text |
url | the request URI |
script_name | the PHP script that handled the request |
report_id | the record number |
That’s why the jq command uses .["0"] and .["1"]:
jq -r '.["0"], .["1"], .url' var/report/PASTE_THE_RECORD_NUMBERNo jq on the server? python3 -m json.tool var/report/PASTE_THE_RECORD_NUMBER works too.
Three traps here.
The file might not be in the root of var/report. pub/errors/processor.php supports nested folders built from pairs of characters of the record number. With a nesting level of 2, a report for 44ffb108... is saved in var/report/44/ff/44ffb108.... The level comes from the MAGE_ERROR_REPORT_DIR_NESTING_LEVEL environment variable or from dir_nesting_level in pub/errors/local.xml, from 0 to 32. If ls var/report doesn’t show your file, use the find command above.
The file shows the latest occurrence only. Because the same error gets the same number, Magento writes to the same file again. If you need history, use exception.log.
Function arguments are hidden by default. The trace is built with Debug::trace(), and arguments are only included when the MAGE_DEBUG_SHOW_ARGS environment variable is set. Turn it on only while you debug, because arguments can contain customer data, tokens or passwords.
Step 3: See the whole exception chain in developer mode
The report file has one message and one trace: the top exception. Many Magento errors are wrapped, and the exception that really caused the problem sits in getPrevious(). exception.log already has the full chain. On a copy of the store where you can reproduce the problem, developer mode shows it right in the browser.
In developer mode, ExceptionHandler::handleDeveloperMode() skips the report page and prints every exception in the chain as plain text, starting with a line like 2 exception(s):, then each exception with its class, message and trace.
On a local or staging copy, check the mode and switch it:
bin/magento deploy:mode:show
bin/magento deploy:mode:set developerThen reproduce the request and read the whole chain.
Two warnings.
First, don’t do this on production. The full exception, with file paths and queries, goes straight to the visitor’s browser.
Second, check where the mode comes from. Bootstrap::isDeveloperMode() reads MAGE_MODE from the server environment first and only falls back to app/etc/env.php when it’s not set. If nginx or PHP-FPM passes MAGE_MODE, it wins over what deploy:mode:show reports. A store can say “production” in env.php and still answer web requests in developer mode because of one line in a server config.
pub/errors/local.xml: the file most developers never open
The behaviour of this page is controlled by pub/errors/local.xml. A clean install doesn’t have it. It only ships pub/errors/local.xml.sample.
These are the options processor.php reads:
| Option | Default | What it does |
|---|---|---|
skin | default | Which folder in pub/errors renders the page |
report/action | empty | Empty shows “Exception printing is disabled…”. print shows the error message. email sends the error by email |
report/subject | Store Debug Information | Email subject, followed by the record number |
report/email_address | empty | Where the email goes. If empty, email is ignored |
report/trash | leave | leave keeps the report file. delete removes it after the email is sent |
report/dir_nesting_level | 0 | Folder nesting for var/report (the environment variable wins) |
Now the trap. The sample file sets <action>print</action>. If someone runs cp pub/errors/local.xml.sample pub/errors/local.xml on a live store to “see the error”, every visitor who hits an exception will see the exception message on the page. That’s the security reason the default text talks about.
Also note what print shows: only the message (reportData[0]), not the trace. It helps less than people expect. The log and the report file are better tools.
The email action is interesting for small stores without monitoring, but read the code before you use it:
- It uses PHP’s
mail()function, so the server must be able to send email. - An email with the error and the trace is sent every time the error page is rendered with that action, so a busy error can fill an inbox fast.
- The visitor sees a contact form (“We are currently experiencing some technical issues…”) and can send their name, email and a comment, which also goes to that address.
For a real store, I prefer proper log monitoring. But it’s good to know this exists, especially when you inherit a project and suddenly receive “Store Debug Information” emails.
When you don’t see this page at all
Not every 500 error goes through ExceptionHandler. Knowing the difference saves time.
“An error has happened during application run. See exception log for details.” This plain-text message comes from Bootstrap::terminate(). It appears when the problem is a PHP \Throwable that is not an \Exception (like a TypeError), or when something fails while Magento is handling the first error. Same advice: go to var/log/exception.log.
A blank page or your web server’s own 500 page. Fatal errors such as Allowed memory size exhausted stop PHP, and Magento’s handlers can’t catch them. Look at the PHP-FPM and web server error logs.
“Error 503: Service Unavailable”. That’s maintenance mode (var/.maintenance.flag), handled before the report, by the same ExceptionHandler.
The 404 page from pub/errors. That’s a Magento\Framework\Exception\State\InitException, which ExceptionHandler handles before the report. It’s logged as critical, so it’s in exception.log too.
Checklist before you close the ticket
- Did you search
var/log/exception.logfor the record number and read the full trace? - Did you count how many times it happened, to understand the impact?
- Did you check the previous exceptions, not only the top message?
- Is
pub/errors/local.xmlabsent on production, or at least not set toprint? - If you turned on
MAGE_DEBUG_SHOW_ARGSor developer mode to debug, did you turn it off again? - Is your monitoring alerting on HTTP 500 and on new lines in
exception.log? - Is someone cleaning
var/report? Withtrashset toleave, Magento’s error page code never deletes those files.
I’ve worked with Magento for more than 15 years, and my rule for this page is simple: the record number is the ticket number. The answer is always in the log. You just need to know which file and which line.
What’s the strangest error you found behind this page?
Sources
All code references were checked in Magento Open Source / Adobe Commerce 2.4.7-p10, 2.4.8-p5 and 2.4.9 (2.4.7-p10 from the open source magento/magento2 repository on GitHub, the other two from full Adobe Commerce installs):
vendor/magento/framework/App/Bootstrap.php:run(),terminate(),isDeveloperMode()vendor/magento/framework/App/ExceptionHandler.php:handle(),handleDeveloperMode(),handleGenericReport()vendor/magento/framework/App/ErrorHandler.php: PHP warnings and notices turned into exceptionsvendor/magento/framework/Encryption/Encryptor.php:getHash()andhash()vendor/magento/framework/Logger/LoggerProxy.php,Logger/Handler/System.php,Logger/Handler/Base.phpandapp/etc/di.xml: how the error reachesexception.logvendor/magento/framework/Debug.php:trace()andMAGE_DEBUG_SHOW_ARGSpub/errors/report.php,pub/errors/processor.php,pub/errors/local.xml.sampleandpub/errors/default/report.phtml

Leave a Reply