Problem/Motivation
In some situations, BigPpe fails to replace content, and a JavaScript error like this appears in the console log:
An error occurred during the execution of the Ajax response: LoadJS
(anonymous) @ ajax.js?v=10.3.1:1143
Promise.catch (async)
Drupal.Ajax.success @ ajax.js?v=10.3.1:1141
processReplacement @ big_pipe.js?v=10.3.1:84
checkMutationAndProcess @ big_pipe.js?v=10.3.1:110
(anonymous) @ big_pipe.js?v=10.3.1:134
processMutations @ big_pipe.js?v=10.3.1:133
We're seeing it with the Flag module, but it's been reported with other modules, too:
- #3457703: An error occurred during the execution of the Ajax response: LoadJS
- #3439303: An error occurred during the execution of the Ajax response: LoadJS
- #3440444: Views ajax exposed filter works only once
Doing some debugging, I found that the "LoadJS" exception is only thrown when loadjs tries to load a CSS/JS asset that's already been loaded:
https://github.com/kubetail-org/loadjs/blob/main/src/loadjs.js#L245
There is this code in ajax.js for the add_css and add_js Ajax commands:
add_css(ajax, response, status) {
const allUniqueBundleIds = response.data.map(function (style) {
const uniqueBundleId = style.href + ajax.instanceIndex;
// Force file to load as a CSS stylesheet using 'css!' flag.
loadjs(`css!${style.href}`, uniqueBundleId, {
add_js(ajax, response, status) {
const parentEl = document.querySelector(response.selector || 'body');
const settings = ajax.settings || drupalSettings;
const allUniqueBundleIds = response.data.map((script) => {
// loadjs requires a unique ID, and an AJAX instance's `instanceIndex`
// is guaranteed to be unique.
// @see Drupal.behaviors.AJAX.detach
const uniqueBundleId = script.src + ajax.instanceIndex;
loadjs(script.src, uniqueBundleId, {
Per the comment in add_js, it's assuming that ajax.instanceIndex is unique and will therefor make uniqueBundleId unique. That is not actually the case here. On our site, we're seeing 3 calls to add_css and 1 to add_js, each with ajax.instanceIndex set to 0. Two of those calls are trying to load the same CSS file from Flag: /modules/contrib/flag/css/flag-link.css?sh4z48. That second call fails because loadjs throws an exception. This prevents BigPipe from being able to properly swap out the content. I'm not sure why ajax.instanceIndex is 0 in each case, but it is. Maybe there are some cases where Drupal.ajax is called multiple times, resetting Drupal.ajax.instances?
Steps to reproduce
We can reproduce this error with just the Flag module:
- Install site with Flag module.
- Create Flag that uses "Link type" of "Confirm Form".
- Create 3-4 content items that will have the Flag.
- Go to /node page or create a View that shows that flaggable content and go to that View's page.
- See that all "Flag this item" links after the first one are missing.
- See the "An error occurred during the execution of the Ajax response: LoadJS" in the JavaScript console log once for every item shown after the first one.
Proposed resolution
The loadjs library provides an `isDefined()` method to check whether a bundle ID has already been defined. We can use that method to gate whether to load the script/css again, so there's no need to use to a counter to ensure uniqueness of the bundle ID. See loadjs documentation.
Remaining tasks
- Fix ajax.js code.
- Add tests.
- Merge fix.
| Comment | File | Size | Author |
|---|---|---|---|
| #8 | 3463875-8.ajaxjs-prevent-loadjs-errors.patch | 1.44 KB | jrb |
| #2 | 3463875-2.ajaxjs-prevent-loadjs-errors.patch | 1.4 KB | jrb |
Issue fork drupal-3463875
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
jrbHere is a patch that adds a counter and uses that to make sure that
uniqueBundleIdis always unique.This fixes our issue with Flag and should fix the other referenced issues above.
Note:
This patch applies to 10.x, not 11.x.
Comment #3
jrbComment #4
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue.
While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)
Comment #5
quietone commentedComment #6
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #8
jrbHere's a patch from the MR.
Note that this patch is for 11.x while the patch in #2 above will apply to 10.x.
Comment #9
smustgrave commentedThanks for reporting, as a bug can we get a test case showing the issue
Comment #10
michal_liszka commentedDespite applying patch #2 for Drupal 10, the problem persists when request the controller for a response via AJAX dialogs.
The first request executes correctly, and the response is accurate on the second attempt. However, the same error continues to occur:
An error occurred during the execution of the Ajax response: LoadJSCode snippet:
Comment #11
espurnesAfter updating to Drupal version 10.3.1 (I was before at version 10.2.4), the "An error occurred during the execution of the Ajax response: LoadJS" error is generated on my custom code after CTAs with ajax actions that should replace page content. Big pipe is not enabled. So it should be related with the way I perform the ajax calls (or the addition of the libraries) and the way drupal 10.3 processed them.
Applying patch #2 solves the issue.
Comment #12
spokjeI've stumbled over this when trying to open a configuration on
/admin/config/development/configurationmore than once, even in the Claro theme.Might make it easier to reproduce in core only for testing.
Patch #2 worked for me.
Comment #13
spokjeMR now has a test. Tried to do this in
\Drupal\Tests\system\FunctionalJavascript\ModalRendererTestbut couldn't get the error there, maybe/probably due to the lack of JS/CSS on that page?So I decided to use a bit of
\Drupal\Tests\config\Functional\ConfigImportUITestin a new FunctionalJavascriptTest.Not great, not fast, but we need JS testing for this and this was the only place in core I could reproduce the error.
MR passes, test-only fails where it's expected to fail.
Comment #16
godotislateLooking at the documentation for loadjs, there is a
isDefined()method that checks for existing bundle IDs, so I think the counter might be unnecessary altogether. I created a new MR 9184 based on the original MR, and added isDefined() checks. It passes all tests, including the new one from #13. Git history of the previous MR is intact, so this approach can be easily reverted if the other way is preferred.Comment #18
godotislateComment #19
godotislateComment #20
smustgrave commentedBelieve adding the isDefined check makes sense.
Comment #21
quietone commentedTrying for a descriptive of what is being fixed. I also left 2 comments in the MR about documentation that need attention.
Comment #22
spokjeThanks @quietone, applied your suggestion and added a comment.
Comment #23
smustgrave commentedBelieve additional feedback has been addressed.
Hoping eventually can help close resolved threads but can't help there either :(
Comment #24
nod_In the case of bigpipe we reuse the same ajax object to execute all the ajax commands so that makes sense. What is surprising is why the same file is added in two different ajax requests, it means the ajax page state is not updated for some reason.
I tried with layout_builder_browser and could replicate the problem without removing the ajaxInstanceIndex from the bundle ID. Why is the ajaxIndex removed from the bundle Id?
Comment #25
godotislateReplied in MR comment, but the idea is that the same resource is being prevented from being loaded twice by using
loadJs.isDefined()to check whether the bundle ID already exists. The counter then should become unnecessary.Is the problem still replicable with layout_builder_browser even with the latest MR? If so, can you provide steps to reproduce?
Comment #26
spokjeComment #27
pcate commentedI just ran into this error when loading a view block on Drupal 10.3.5. Layout Builder is not used on the site, the block was added via the regular block layout page. The big pipe placeholder element is rendered on the page but the error prevents it being replaced with the actual content.
The MR resolved the error and loaded the block fine.
Comment #28
godotislateUpdated the IS with the solution implemented the MR.
Comment #29
nod_Ok, Happy with the JS changes.
For the test, I understand but i'm not comfortable using a sideeffect of the config UI to test an ajax regression. Can we make a more explicit test by trying to load the same file twice through add_js and add_css? It's a missing test so I guess that's why you had to create a new one: #3301769: Add test for the new add_js command
Comment #30
spokjeI can understand this isn't a nice direct test, but it was the only way I could reproduce it in core.
I'm a tad confused why add_js/add_css could reproduce the issue when the commands are new, whilst I think the config UI didn't change that much in recent time?
Anyway, I'm sure someone with actual knowledge will pick this up in the end.
Comment #31
nod_yeah i'd rather have this in than not in
Comment #37
nod_Committed and pushed d44fb628e45 to 11.x and 0b0d880cbfd to 11.0.x and b60151a2656 to 10.4.x and 9b977fc3f84 to 10.3.x. Thanks!
Comment #38
interlated commentedThis fixed a problem with ajax modals
- Install layout builder, linked_field
- Link the title in layout builder.
- install the code below in the custom theme
- clear cache
- Open modal once
- Close modal
- Open again.
A cryptic 'LoadJS' error message is returned on the console. Line 1143 ajax.js.
Comment #40
ambient.impactAh, I see I'm not the only one that was utterly confused by the cryptic "LoadJS" error. It turns out the library did this to keep the size down, which I personally feel was a bad move because it's so confusing and non-helpful. I opened a pull request to change it, but since they still want to keep the size down, they're only willing to throw the bundle ID as the error message rather than a fully descriptive thing that says why it was thrown. Glad to see this is being handled on the Drupal side.