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: external and minified: true (which is valid),
  • Is defined in .libraries.yml but 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_ext

Asset 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

  1. Define a library that includes an external CSS file marked minified: true.
  2. 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.

Issue fork drupal-3414173

Command icon 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

joelpittet created an issue. See original summary.

joelpittet’s picture

Status: Active » Needs review
StatusFileSize
new767 bytes

Here's a patch to workaround this issue.

joelpittet’s picture

Ideally it would be nicer to consider the include invalid cache and rebuild the asset or something...

needs-review-queue-bot’s picture

Status: Needs review » Needs work

The 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.)

djsagar’s picture

Version: 10.2.x-dev » 11.x-dev
joelpittet’s picture

@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

joelpittet’s picture

Status: Needs work » Needs review

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs tests

Thanks for reporting. Will need a test case showing the problem.

joelpittet’s picture

@smustgrave any hints at where tests of this nature should go or could extend?

Test Scenario:

  • External Library + minified included in the URL = Fail as expected
  • External Library + without minified included in the URL = Skipped
smustgrave’s picture

Maybe in Drupal\FunctionalTests\Asset;

Also doesn't have to be it's own test, could be an additional assertion somewhere.

firewaller’s picture

Does 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...

gpapadas’s picture

I 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).

jlancaster’s picture

Apologies... wrong issue that I posted this on...

anybody made their first commit to this issue’s fork.

ssantaeugenia’s picture

Thanks for this great patch. I've attached the modified patch to also affect JavaScript.

osopolar’s picture

I 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: external AND minified: true and so you removed the minimized there. We have one such asset, the better_exposed_filters library. But I am wondering why minified: true shouldn't be set for external libraries. There is a example of both attributes set in documentation under minified, with the description

Whether the asset is already minified. Default: false

The 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?

joelpittet’s picture

@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.

catch’s picture

Status: Needs work » Postponed (maintainer needs more info)

Yes minified describes the library, not what to do with it.

firewaller’s picture

At the very least the condition to check "external" should be considered in \Drupal\Core\Asset\CssCollectionOptimizerLazy::optimizeGroup calls

joelpittet’s picture

Status: Postponed (maintainer needs more info) » Needs work

I’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:43

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
        )

)

This 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_ext

Which attempts to load a library from my theme:
galactus_cs_ext/clf-fw-bw
That library is no longer used. So this request seems to be coming from a cached, malicious, or stale reference, and causes the

“Only file CSS assets can be optimized”

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 use UrlHelper::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:

clf-fw-bw:
  version: 7.0.4
  css:
    base:
      https://cdn.ubc.ca/clf/7.0.4-minimal/css/minimal-clf-full-7.0.4-bw.css: { type: external, minified: true }
    theme:
      css/clf/clf.social-icons.css: {}
  dependencies:
    - galactus_cs_ext/cdn-clf-js

Our docs suggest to me that this looks right:
https://www.drupal.org/docs/develop/theming-drupal/adding-assets-css-js-...

joelpittet’s picture

Status: Needs work » Needs review

Took a stab because I noticed in debugging JsCollectionOptimizerLazy had an exclusion that CssCollectionOptimizerLazy was missing.

Fixing that prevents this bug, I will see if I can write a test to prove it's fixed.

joelpittet’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
joelpittet’s picture

joelpittet’s picture

joelpittet’s picture

joelpittet’s picture

Issue tags: -Needs tests

Ok 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().

anybody’s picture

Status: Needs review » Needs work

Thanks @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...

joelpittet’s picture

Status: Needs work » Needs review

The 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.

nicxvan changed the visibility of the branch 11.x to hidden.

nicxvan’s picture

Title: Website error Exception: "Only file CSS assets can be optimized" » Add support for minified external CSS libraries
Status: Needs review » Reviewed & tested by the community

I 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.

  • catch committed a1b3926d on 11.2.x
    Issue #3414173 by joelpittet, anybody, nicxvan: Add support for minified...

  • catch committed 8986a105 on 11.x
    Issue #3414173 by joelpittet, anybody, nicxvan: Add support for minified...
catch’s picture

Version: 11.x-dev » 11.2.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 11.x and cherry-picked to 11.2.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

herved’s picture

This 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: true from those files, even though they’re already minified by my build process.

Was this behavior change intentional?

joelpittet’s picture

@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 -R
And after testing it re-patch
curl https://git.drupalcode.org/project/drupal/-/commit/a1b3926d59e341d771963eb8a3de9d881be7a107.diff | patch -p1

herved’s picture

@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::optimize does 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: true and expect/require url() in their CSS to be rewritten.

benabaird’s picture

This 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.

benabaird’s picture

I've opened a follow up issue https://www.drupal.org/project/drupal/issues/3535330 for this.

joelpittet’s picture

@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

joelpittet’s picture

dgroene’s picture

StatusFileSize
new1014 bytes

Patch for 10.4.8

dgroene’s picture

StatusFileSize
new1.01 KB

Patch for 10.4.8 at level p2