Problem/Motivation
After upgrading to Drupal 10.2, sites may encounter recurring errors in logs like:
Exception: Only file CSS assets can be optimized. in Drupal\Core\Asset\CssOptimizer->optimize() (line 43 of core/lib/Drupal/Core/Asset/CssOptimizer.php).
This occurs when stale or cached requests for CSS aggregate files include a library that:
- Contains a CSS asset with
type: externalandminified: true(which is valid), - Is defined in
.libraries.ymlbut no longer actively used, - Is referenced via an old
include=query string from a cached page or bot request.
The optimizer attempts to rebuild the group based on the include hash and fails when it tries to optimize the external CSS asset.
Example of a failing request:
sites/default/files/css/css__xlnlyYvQDTExRxMzowYQQbU2Gkpxbvzgs_51zHi7ms.css?delta=1&include=eJxlj-EOwiAMhF-IySORwsqGFkpo56ZP79QZjfy7-y6X9iK3rKmKjYcwsCg7KGHmJvbXmACEZYRmP-KkM2Y0DaVykXRFp-AJXUyk2OyXDy9u5CaK2XoQNIEb2rIfBUp3NBMQBF3EBXG4qfXMKtqg9smz_Q8zisCE0gUTsQcyu6RULnZsSwU6Hfb9xMHgDFtXDxSHuA5-7ZJ9Iz4A2b18gw&language=en&theme=galactus_cs_extAsset that causes the failure:
Array ( [type] => external [minified] => 1 [weight] => -199.99963333333 [group] => 100 [data] => https://cdn.ubc.ca/clf/7.0.4-minimal/css/minimal-clf-full-7.0.4-bw.css [version] => 7.0.4 [media] => all [preprocess] => 1 [license] => Array ( [name] => GPL-2.0-or-later [url] => https://www.drupal.org/licensing/faq [gpl-compatible] => 1 ) )
Definition of the affected library:
clf-cdn-fw-bw: version: 7.0.4 css: base: https://cdn.ubc.ca/clf/7.0.4/css/ubc-clf-full-bw.min.css: { type: external } dependencies: - galactus/cdn-clf-js
Steps to reproduce
- Define a library that includes an external CSS file marked
minified: true. - Make a request using a stale
include=URL that references the old aggregate.
Proposed resolution
Took a stab at a fix after noticing during debugging that JsCollectionOptimizerLazy includes an exclusion for assets marked minified, but CssCollectionOptimizerLazy does not.
The proposed fix is to add similar logic to CssCollectionOptimizerLazy so that it skips already-minified external assets (which cannot be optimized anyway). This mirrors the handling in the JavaScript optimizer and prevents the fatal error when such assets are encountered.
This would allow the system to safely process cached/stale aggregate requests referencing external CSS without the errors.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3414173
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
joelpittetHere's a patch to workaround this issue.
Comment #3
joelpittetIdeally it would be nicer to consider the include invalid cache and rebuild the asset or something...
Comment #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
djsagar commentedComment #6
joelpittet@djsagar Why move it to 11.x-dev it's a bug, did the policy change or doesn't it get applied to the latest release's branch? I maybe missed a memo
Comment #7
joelpittetFound the memo :) https://www.drupal.org/node/767608#d8-minor
Comment #9
smustgrave commentedThanks for reporting. Will need a test case showing the problem.
Comment #10
joelpittet@smustgrave any hints at where tests of this nature should go or could extend?
Test Scenario:
Comment #11
smustgrave commentedMaybe in Drupal\FunctionalTests\Asset;
Also doesn't have to be it's own test, could be an additional assertion somewhere.
Comment #12
firewaller commentedDoes it make sense to relocate this change to `\Drupal\Core\Asset\CssCollectionOptimizerLazy::optimizeGroup` similar to the JS approach? https://git.drupalcode.org/project/drupal/-/blob/11.x/core/lib/Drupal/Co...
Comment #13
gpapadas commentedI am reporting a similar (as it seems) issue when I visit the Resend page of a webform.
It occurs with Claro and Gin themes. When I try with eg. the Bootstrap admin theme, the issue is not present.
As a result, when I select another email than the default, it doesn't work. Also, the editor's layout (of the email) breaks.
These are the errors I get:
1. Only file JavaScript assets can be optimized. in Drupal\Core\Asset\JsOptimizer->optimize() (line 31 of /var/www/html/docroot/core/lib/Drupal/Core/Asset/JsOptimizer.php).
2. Symfony\Component\HttpKernel\Exception\BadRequestHttpException: Invalid filename. in Drupal\system\Controller\AssetControllerBase->getGroup() (line 224 of /var/www/html/docroot/core/modules/system/src/Controller/AssetControllerBase.php).
Comment #14
jlancaster commentedApologies... wrong issue that I posted this on...
Comment #16
ssantaeugenia commentedThanks for this great patch. I've attached the modified patch to also affect JavaScript.
Comment #17
osopolarI have the same errors in the log and now I am wondering how to fix it.
@joelpittet You mentioned that you had assets that were
type: externalANDminified: trueand so you removed the minimized there. We have one such asset, the better_exposed_filters library. But I am wondering whyminified: trueshouldn't be set for external libraries. There is a example of both attributes set in documentation under minified, with the descriptionThe asset in your example (https://cdn.ubc.ca/clf/7.0.4/css/ubc-clf-full-bw.min.css) seems to be already minified, so why not setting
minified: true?Comment #18
joelpittet@osopolar that’s a really good point — I should know better than to make assumptions about how this works. I was under the impression minified was an action to be taken rather than a descriptor. But you’re right, the docs clearly state otherwise. Looks like I’ve been shooting myself in the foot here! Thanks so much for pointing it out.
That said, I still think the page-cached asset URLs are a problem, regardless of my incorrect understanding. I’ll try to update the issue summary to reflect that.
Comment #19
catchYes minified describes the library, not what to do with it.
Comment #20
firewaller commentedAt the very least the condition to check "external" should be considered in \Drupal\Core\Asset\CssCollectionOptimizerLazy::optimizeGroup calls
Comment #21
joelpittetI’m digging into this because the error still appears in my logs month to month.
Here’s the asset that triggers the failure:
core/lib/Drupal/Core/Asset/CssOptimizer.php:43This is coming off a request to:
/sites/default/files/css/css__xlnlyYvQDTExRxMzowYQQbU2Gkpxbvzgs_51zHi7ms.css?delta=1&include=eJxlj-EOwiAMhF-IySORwsqGFkpo56ZP79QZjfy7-y6X9iK3rKmKjYcwsCg7KGHmJvbXmACEZYRmP-KkM2Y0DaVykXRFp-AJXUyk2OyXDy9u5CaK2XoQNIEb2rIfBUp3NBMQBF3EBXG4qfXMKtqg9smz_Q8zisCE0gUTsQcyu6RULnZsSwU6Hfb9xMHgDFtXDxSHuA5-7ZJ9Iz4A2b18gw&language=en&theme=galactus_cs_extWhich attempts to load a library from my theme:
galactus_cs_ext/clf-fw-bwThat library is no longer used. So this request seems to be coming from a cached, malicious, or stale reference, and causes the
error.
For reference, here’s how you can decode the include hash via the command line:
php -r "print_r(@gzuncompress(base64_decode(str_replace(['-', '_'], ['+', '/'], 'INCLUDE_HASH_GOES_HERE'))));"Or you can useUrlHelper::uncompressQueryParameter()in code.I’m not sure how to categorize this type of bug—it stems from a library that used to be used, still exists, but is no longer active in the theme. Yet the request for it persists and causes an exception rather than a graceful fallback or warning.
You might see why I confused as to the "minified" as it looked like a type: "external" is being "minified", and is thrown in a
if ($css_asset['type'] != 'file') {The library definition is:
Our docs suggest to me that this looks right:
https://www.drupal.org/docs/develop/theming-drupal/adding-assets-css-js-...
Comment #22
joelpittetTook a stab because I noticed in debugging
JsCollectionOptimizerLazyhad an exclusion thatCssCollectionOptimizerLazywas missing.Fixing that prevents this bug, I will see if I can write a test to prove it's fixed.
Comment #23
joelpittetComment #24
joelpittethttps://git.drupalcode.org/project/drupal/-/commit/b957382#2fb629f0e45c5... This is where the JS part got committed for reference: #1014086: Stampedes and cold cache performance issues with css/js aggregation
Comment #25
joelpittetComment #26
joelpittetComment #27
joelpittetOk wrote a test, hope the local file I faked as external is appropriate in this case. Couldn't think of what to do for an "external" CSS file but it doesn't really need to be truely external, just something to give to
file_get_contents().Comment #28
anybodyThanks @joelpittet great work and test is green!
I left some final, minor comments, what do you think?
We're having these log entries day by day...
Comment #29
joelpittetThe tests-only fail (as expected) by the way.
Thanks @Anybody for review, I put code suggestions that you can apply (might need push access to do so), but feel free to apply the suggestions or make your own in the comments you started.
Comment #31
nicxvan commentedI think this is ready now, I compared with the JS lazy optimizer and the logic looks the same.
I don't think bugfixes like this need CRs.
Test only fails as expected.
I took a chance on updating the title too.
Comment #34
catchCommitted/pushed to 11.x and cherry-picked to 11.2.x, thanks!
Comment #37
herved commentedThis change seems a bit unexpected to me.
I didn’t read the full issue yet, but I have a theme that compiles and minifies SASS to CSS using Webpack, so I was using
minified: true, assuming it would prevent additional minification by core.After upgrading from D10 to D11.2, I noticed that url() references in my @font-face declarations are broken when CSS aggregation is enabled.
In 11.2, the CSS files retain relative url() paths, regardless of whether aggregation is enabled — which wasn’t the case before.
For now, I’ve removed
minified: truefrom those files, even though they’re already minified by my build process.Was this behavior change intentional?
Comment #38
joelpittet@herved Your expectation is how it should work, this change was creating parity in how CSS works as that is how JS works (I lifted the code from what we had in JS already). Can you revert the diff on your machine to see if it truely is this commit? (I was confused about "minified" coming into this issue in the first place, I think I got a better grasp coming out.
You can grab the commit URL and add .diff to the end to get a patch and reverse it like this:
curl https://git.drupalcode.org/project/drupal/-/commit/a1b3926d59e341d771963eb8a3de9d881be7a107.diff | patch -p1 -RAnd after testing it re-patch
curl https://git.drupalcode.org/project/drupal/-/commit/a1b3926d59e341d771963eb8a3de9d881be7a107.diff | patch -p1Comment #39
herved commented@joelpittet yes I'm sure it's this commit and reading the code, it makes total sense.
IIUC the only thing
\Drupal\Core\Asset\CssOptimizer::optimizedoes is to fix/rewrite URLs when aggregating, which is always welcome for local CSS files IMO. Maybe this is why it didn't match the same logic as JS?IDK but I suspect this is going to cause issues for others as well when upgrading to D11.2, at least if they used
minified: trueand expect/require url() in their CSS to be rewritten.Comment #40
benabaird commentedThis is causing an issue for us with the Font Awesome Icons module loading minified CSS from
/libraries/; the relative urls for webfonts are no longer rewritten upon aggregation.If I understand the original issue correctly I think the fix on the issue should be to check if the asset is of type external before attempting to optimize it, as that's what's triggering the exception, rather than checking if it's minimized. This isset() check might work indirectly for external assets, but I'd guess by some other side-effect of external assets never having a the minified key set.
Comment #41
benabaird commentedI've opened a follow up issue https://www.drupal.org/project/drupal/issues/3535330 for this.
Comment #42
joelpittet@herved and @benabaird I see what you mean—sorry to cause so much trouble with this, and thanks for following up (and for opening a new issue to track it).
I ran into the same thing with fontawesome as you did, @benabaird. When I wrote the patch and test, I copied how JS assets were handled, and there wasn’t anything in the comments or tests at the time to highlight this gotcha for CSS files—especially for local minified files relying on URL rewriting during aggregation.
I wouldn’t want this commit reverted, as it does solve a different and legitimate problem. But I do agree that targeting external assets specifically would be a more precise fix, and wouldn’t break local asset aggregation that people are relying on.
Appreciate you both raising this and suggesting a focused fix. I will see you over on the follow-up issue! #3535330: Assets paths in CSS no longer rewritten when aggregation is enabled
Comment #43
joelpittetComment #44
dgroene commentedPatch for 10.4.8
Comment #45
dgroene commentedPatch for 10.4.8 at level p2