[4.x]: 400 "AxiosError" when opening CP modals for a second time.
What happened?
Description
Performing actions in the CP that do not require a page refresh (i.e. asset or entry popup modals, editing an image, switching tabs or volumes, etc) eventually cause the UX to be come unresponsive, with an 'AxiosError' in the JS console. Once it's unresponsive, if you try to save an element, a flash message "Couldn’t save entry" is displayed, and you have no way to proceed except to refresh the page and try again.
This started happening recently after applying Craft Updates. No other updates were made on the server. Also happens locally and on server, so environment issue is inlikely.
Currently running version: Craft CMS 4.5.12
Steps to reproduce
There's not one specific path to reproduce it. Performing a number of UX tasks without causing a screen refresh will eventually cause it. Usually within a minute or two for me.
But some example steps I'm performing are...
- Open an entry that has a matrix field with an image field in one of the matrix blocks.
- Select an image in that matrix
- Click around switching tabs in the entry, trying to edit the image, removing the image and reselecting it from a different volume, etc.
- Eventually the UX stops responding, and you get the AxiosError below in the JS console.
-OR-
- Click asset selector
- Select an asset
- Save asset
- Open asset selector again (you may need to save/reopen a couple times)
- Nothing loads
- JS console shows 400 AxiosError
Expected behavior
Work properly without having to refresh the page to avoid the UX stalling.
Actual behavior
When clicking around the UX performing various actions that do not require a page refresh, the following error eventually displays in the JS console and causes the UX to become unresponsive...
Uncaught (in promise) o {message: 'Request failed with status code 400', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}code: "ERR_BAD_REQUEST"config: {transitional: {…}, transformRequest: Array(1), transformResponse: Array(1), timeout: 0, adapter: ƒ, …}message: "Request failed with status code 400"name: "AxiosError"request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}response: {data: {…}, status: 400, statusText: '', headers: {…}, config: {…}, …}[[Prototype]]: Error
Craft CMS version
4.5.12
PHP version
8.0.25
Operating system and version
Linux 3.10.0-1160.80.1.el7.x86_64
Database type and version
MariaDB 10.10.2
Image driver and version
Imagick 3.7.0 (ImageMagick 6.9.12-67)
Installed plugins and versions
Comments 2.0.9 Contact Form 3.0.1 Instant Analytics GA4 4.0.0 Logs 4.0.0 Redactor 3.0.4 SendGrid 2.0.1 Sidebar Entry Types 2.0.1
I'm getting this error again now just while navigating through menu items, and the only resolution is a page refresh. The last issue I reported turned out to be a legit bug, but took a long time for anyone to get to it. I hope this one is addressed sooner, as it's becoming near unusable when you constantly have to refresh the page between actions.
@jacksutherland I tried to replicate on my end but wasn't able to. Are you able to provide any additional details around getting this to happen consistently?
Is there any pattern to the requests that eventually get a 400? Or could you provide a route or two that you're seeing it on?
The 400 error will typically have a message on it at error.response.data.message, does that message exist? What message do you get?
@brianjhanson I'm still dealing with this unfortunately. The only pattern is that it doesn't error for about 5 minutes, and it only happens while opening or saving modal type content i.e. editing an image, selecting an entry or asset, etc. I've been so busy that I've just been trying to work around it, refreshing when it occurs and trying to do whatever I was doing faster. Which is obviously not a long-term solution.
error.response.data.message just says "Unable to verify your data submission." Here's a screenshot...
Could the server "Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/8.0.25" be an issue? I haven't noticed it locally.
@jacksutherland if you’re not seeing it locally, you’re correct it's most likely an issue related to the environment in some way.
Can you capture the URL that is returning the 400 error? A request isn’t including the CSRF token as it should, or the CSRF token isn’t renewing as expected somewhere.
Is there anything else notable about your production environment? Caching layers, unique configuration, etc.?
@brianjhanson no, it's a typical CentOS setup. And the site has been fine for years. It only started doing this over the past few months, with no server changes.
The most common way I reproduce it is by going to an entry, clicking an image in an asset field, then click "Edit Image". It won't error if I save quickly. The error needs a few minutes without a refresh to happen. So, if I use the image editing features for a few minutes, then save, it throws the error.
The response URL in the above scenario is: https://bandpioneer.com/index.php?p=admin%2Factions%2Fassets%2Fimage-editor&site=default&v=1708970436152
@jacksutherland thanks for the details!
What you're seeing aligns with the CSRF token either expiring, or not getting sent along with the request at all. The odd thing is that the token should be included in an x-csrf-token header, which is grabbed from the Craft global javascript object right before the request is sent.
Do you see an x-csrf-token header on the request that returns an error? If you do, does it match the value at Craft.csrfTokenValue?
Do you manage the server yourself or through a hosting provider? If you're using a provider, I'm curious if something could have changed on their end recently.
We might need access to the environment to get to the bottom of this. If you're willing to give us SSH access can you write in to [email protected] and reference this issue?
@brianjhanson its just a droplet at Digital Ocean. I can give SSH access.
I do not see x-csrf-token. This site does use Ezoic for ads, which I'm slightly suspicious of. But it's set to not interfere with the CP, and that's also been setup long before the bug started.
I believe this was resolved in support, but feel free to re-open if you're still having issues @jacksutherland