Mini-css-extract-plugin: "Cannot read property 'pop' of undefined" with a common cache group

Created on 30 Aug 2018  ·  41Comments  ·  Source: webpack-contrib/mini-css-extract-plugin

I ran into this after upgrading to 0.4.2.

ERROR in chunk common [initial]
common.css
Cannot read property 'pop' of undefined
TypeError: Cannot read property 'pop' of undefined
    at MiniCssExtractPlugin.renderContentAsset (.../node_modules/mini-css-extract-plugin/dist/index.js:343:44)
    at Object.render (.../node_modules/mini-css-extract-plugin/dist/index.js:175:32)
    at Compilation.createChunkAssets (.../node_modules/webpack/lib/Compilation.js:2358:29)
    at hooks.optimizeTree.callAsync.err (.../node_modules/webpack/lib/Compilation.js:1268:10)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (.../node_modules/tapable/lib/Hook.js:35:21)
    at Compilation.seal (.../node_modules/webpack/lib/Compilation.js:1213:27)
    at hooks.make.callAsync.err (.../node_modules/webpack/lib/Compiler.js:547:17)
    at _done (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:9:1)
    at _err16 (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:218:22)
    at _addModuleChain (.../node_modules/webpack/lib/Compilation.js:1064:12)
    at processModuleDependencies.err (.../node_modules/webpack/lib/Compilation.js:980:9)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

common.css is set up like so:

optimization: {
  splitChunks: {
    cacheGroups: {
      common: {
        name: 'common',
        chunks: 'initial',
        minChunks: 3,
        priority: 10,
        reuseExistingChunk: true
      }
    }
  }
},

And has an explicit entry with the following:

common: [
  path.resolve(srcPath, 'vendor.scss'),
  path.resolve(srcPath, 'main.scss')
]
3 (important) patch 3 (broken) bug

Most helpful comment

Webpack is the biggest joke played on the JavaScript community so far.

All 41 comments

@OscarBarrett Looks like a bug

Same problem appeared after update to Babel 7 i don't know if there is a link

Same problem here even without a cache group.

Build fails with:

Cannot read property 'pop' of undefined
TypeError: Cannot read property 'pop' of undefined
at MiniCssExtractPlugin.renderContentAsset (node_modules/mini-css-extract-plugin/dist/index.js:343:44)

Please create minimum reproducible test repo

https://github.com/OscarBarrett/mini-css-issue-257

yarn build

Note that the build succeeds if the common entry is commented out.

Ran into this today too.

webpack: 4.17.2
mini-css-extract-plugin: 0.4.2

Little more insight into what may be happening:
screenshot from 2018-09-07 19-34-44
screenshot from 2018-09-07 18-13-22

bestMatch never gets a value because sortedModules is empty (note that in my case, the function input param modules in renderContentAsset has 3 items in it).
This is because getModuleIndex2 returns undefined for every module that gets passed in (I verified this manually by running all the modules through it).

I was able to fix this by locally editing index.js and changing this if statement to always return false
https://github.com/webpack-contrib/mini-css-extract-plugin/blob/dd141f2aacddbb30d9129a14425085b11f3cba8d/src/index.js#L397

So it looks like the fallback logic
https://github.com/webpack-contrib/mini-css-extract-plugin/blob/dd141f2aacddbb30d9129a14425085b11f3cba8d/src/index.js#L480-L485
still works.

@ndisidore feel free to send a PR :+1:

I can totally help with a PR. May need a little direction on the correct way to do it vs the quick fix.

It looks like getModuleIndex2 is just a .get on the map of the chunkGroup. So essentially what we're trying to do is see if the current module is in the given chunk group. Under what circumstances would this never be the case and are those expected?

Some more context around those specifics.
screenshot from 2018-09-10 18-58-29

When will this get resolved? I'm running into the same issue here.

@aganglada When somebody send a PR

@OscarBarrett Found problem. Maybe related to other people.

entry: {
    first: './first.jsx',
    second: './second.jsx',
    common: './common.css'
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

You grouped modules to common chunk and use common: './common.css' entry. Just change name common entry to commonStyle

Best avoid using an entrypoint as name for a cacheGroup. You can rewrite this config to this:

entry: {
    first: ['./common.css', './first.jsx'],
    second: ['./common.css', './second.jsx'],
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

btw. in webpack 5 there will be an error when using an entrypoint name as cacheGroup name.

Woo, must have missed reading it somewhere in the docs, but

Best avoid using an entrypoint as name for a cacheGroup. You can rewrite this config to this:

Seems to solve most of the problems I've been having either with this error or emitting to the same chunk.

Interesting thing. I'm facing this issue only for production mode.
When in my config I set mode: "production" build fails, but when mode: "development" everything works ok.
So, with my particular configuration getModuleIndex2 works ok in dev mode and returns undefined in prod. Seems that some webpack plugins mess up ChunkGroup._moduleIndicies2 map or something like that. Besides, in production mode there are no css modules in _moduleIndicies2 at all

My style loaders and cacheGroups are

const packageNames = [
  'foo',
  'bar'
]

{
  optimization:
  {
    splitChunks:    {
      cacheGroups: packageNames.reduce((acc, pkg) => Object.assign(acc,
      {
        [pkg]:
        {
          name: pkg,
          test: new RegExp(`node_modules(\\\\|\\\/)${pkg}(\\\\|\\\/)`),
          chunks: 'all',
          priority: 10,
        }
      }),
      {})
    },
  },
  module:  {
    rules: [
    {
      test: /\.css$/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader'
      ]
    },
    {
      test: /\.less/,
      use: [
        MiniCssExtractPlugin.loader,
        'css-loader',
        'less-loader',
      ]
    }, ]
  }
  plugins: [
    new MiniCssExtractPlugin(
    {
      filename: 'static/css/[name].css',
    }),
  ]
}

PS I have no entrypoint names used as keys for cacheGroups

Using [email protected], [email protected]

Moreover , everything works fine when I add { enforce: true } in my splitChunks entries or remove { chunks: 'all' } from them

Ran into this issue myself and going off of what @ndisidore posted I think I see another issue at play, at least with my setup.

webpack.config.js

optimization: {
        minimize: true,
        minimizer: [
            new OptimizeCSSAssetsPlugin({})
        ],
        splitChunks: {
            cacheGroups: {
                mainStyles: {
                    name: 'main',
                    test: (m,c,entry = 'main') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }

The issue I am seeing revolves around this block in the index.js of the plugin.

      for (const list of modulesByChunkGroup) {
          // skip and remove already added modules
          while (list.length > 0 && usedModules.has(list[list.length - 1])) list.pop();

          // skip empty lists
          if (list.length !== 0) {
            const module = list[list.length - 1];
            const deps = moduleDependencies.get(module);
            // determine dependencies that are not yet included
            const failedDeps = Array.from(deps).filter(unusedModulesFilter);

            // store best match for fallback behavior
            if (!bestMatchDeps || bestMatchDeps.length > failedDeps.length) {
              bestMatch = list;
              bestMatchDeps = failedDeps;
            }
            if (failedDeps.length === 0) {
              // use this module and remove it from list
              usedModules.add(list.pop());
              success = true;
              break;
            }
          }
        }

I'm finding that in some cases, all of the arrays in modulesByChunkGroup are empty. When this happens, bestMatch is never set and success=true is also never reached so the .pop error occurs. I've tried to do some empty array checks but that doesn't seem to be the right way to tackle this issue. Hoping this will help track down the right fix for this issue.

Ran into this issue myself and going off of what @ndisidore posted I think I see another issue at play, at least with my setup.

webpack.config.js

optimization: {
        minimize: true,
        minimizer: [
            new OptimizeCSSAssetsPlugin({})
        ],
        splitChunks: {
            cacheGroups: {
                mainStyles: {
                    name: 'main',
                    test: (m,c,entry = 'main') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }

The issue I am seeing revolves around this block in the index.js of the plugin.

      for (const list of modulesByChunkGroup) {
          // skip and remove already added modules
          while (list.length > 0 && usedModules.has(list[list.length - 1])) list.pop();

          // skip empty lists
          if (list.length !== 0) {
            const module = list[list.length - 1];
            const deps = moduleDependencies.get(module);
            // determine dependencies that are not yet included
            const failedDeps = Array.from(deps).filter(unusedModulesFilter);

            // store best match for fallback behavior
            if (!bestMatchDeps || bestMatchDeps.length > failedDeps.length) {
              bestMatch = list;
              bestMatchDeps = failedDeps;
            }
            if (failedDeps.length === 0) {
              // use this module and remove it from list
              usedModules.add(list.pop());
              success = true;
              break;
            }
          }
        }

Best avoid using an entrypoint as name for a cacheGroup. You can rewrite this config to this:

entry: {
    first: ['./common.css', './first.jsx'],
    second: ['./common.css', './second.jsx'],
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

btw. in webpack 5 there will be an error when using an entrypoint name as cacheGroup name.

hi @sokra , I also encountered the same problem. extract-text-webpack-plugin is all OK but only using [hash]... , and mini-css-extract-plugin throws the error: Cannot read property 'pop' of undefined . there are lots of scenarios using an entrypoint name as cacheGroup name, Why not support it and fix what @cshomo11 posted but remove it in webpack5? Looking forward to your reply, thanks.

@OscarBarrett Find problem. Maybe related to other people.

entry: {
    first: './first.jsx',
    second: './second.jsx',
    common: './common.css'
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

You grouped modules to common chunk and use common: './common.css' entry. Just change name common entry to commonStyle

What if I want to combine css content from entry and also splitChunks cachegroup? If we change entry to commonStyle, basically, it will create common.css and commonStyle.css. Instead, I want these two file contents to be merged to one file (common.css). How can we achieve that?

webpack: 4.29.0
mini-css-extract-plugin: 0.5.0

Setup:

entry: {
      bootstrap: "./javascripts/bootstrap.js",
      .....
}
....
splitChunks: {
        cacheGroups: {
          ...
          bootstrap: {
            chunks: 'initial',
            name: 'bootstrap',
            test: 'bootstrap',
            enforce: true
          }
        }
      }

Error:

ERROR in chunk bootstrap [initial]
stylesheets/bootstrap.css
Cannot read property 'pop' of undefined

Rolled back to webpack 3.x.

@ed-mare double check you webpack version, maybe you have installed global webpack

I call webpack like so node_modules/.bin/webpack which is 4.29.0. node -v v10.15.0. @babel/core": "7.2.2", "@babel/preset-env": "7.3.0", "@babel/register": "^7.0.0", "babel-loader": "8.0.5", "babel-register": "6.26.0".

{
  "presets": [
    ["@babel/preset-env", {
      "useBuiltIns": "entry",
      "targets": {
        "browsers": ["last 2 versions", "ie >= 11", "Edge >= 15"]
      }
    }]
  ]
}
common: "./app/assets/javascripts/common.js",
bootstrap: "./app/assets/javascripts/bootstrap.js",
...
cacheGroups: {
          common: {
            chunks: 'all',
            name: 'common',
            test: 'common',
            enforce: true
          },
          bootstrap: {
            chunks: 'all',
            name: 'bootstrap',
            test: 'bootstrap',
            enforce: true
          }
}
 ```

The interesting thing is that I have two entries where cacheGroup has the same name as the entry. The first one (common) works while the second one (bootstrap) fails with error below:

ERROR in chunk bootstrap [initial]
stylesheets/bootstrap-6dfcaf03088d05e1c8ce.css
Cannot read property 'pop' of undefined
TypeError: Cannot read property 'pop' of undefined
at MiniCssExtractPlugin.renderContentAsset (/home/app/myapp/node_modules/mini-css-extract-plugin/dist/index.js:343:44)
at Object.render (/home/app/myapp/node_modules/mini-css-extract-plugin/dist/index.js:174:32)
at Compilation.createChunkAssets (/home/app/myapp/node_modules/webpack/lib/Compilation.js:2429:29)
at hooks.optimizeTree.callAsync.err (/home/app/myapp/node_modules/webpack/lib/Compilation.js:1299:10)
at AsyncSeriesHook.eval [as callAsync] (eval at create (/home/app/myapp/node_modules/tapable/lib/HookCodeFactory.js:32:10), :6:1)
at AsyncSeriesHook.lazyCompileHook (/home/app/myapp/node_modules/tapable/lib/Hook.js:154:20)
at Compilation.seal (/home/app/myapp/node_modules/webpack/lib/Compilation.js:1244:27)
at hooks.make.callAsync.err (/home/app/myapp/node_modules/webpack/lib/Compiler.js:624:17)
at _done (eval at create (/home/app/myapp/node_modules/tapable/lib/HookCodeFactory.js:32:10), :9:1)
at _err2 (eval at create (/home/app/myapp/node_modules/tapable/lib/HookCodeFactory.js:32:10), :44:22)
at _addModuleChain (/home/app/myapp/node_modules/webpack/lib/Compilation.js:1095:12)
at processModuleDependencies.err (/home/app/myapp/node_modules/webpack/lib/Compilation.js:1007:9)
at process._tickCallback (internal/process/next_tick.js:61:11)

I don' t understand why it works with one and not the other. It complains about the CSS file (sass) which contains CSS @import directives. Maybe I'm missing a config or special handling for this file? All other CSS (sass) files work. 

If I change configs to this (entry name and file name are different):

boot: "./app/assets/javascripts/bootstrap.js",
...
boot: {
chunks: 'all',
name: 'bootstrap',
test: 'bootstrap',
enforce: true
}

webpack executes without error but it splits the javascript into two files, bootstrap.js and boot.js, and one CSS file bootstrap.css. Okay, promising, I can live with that though I prefer one javascript bundle. When I run this in **development mode**,  javascript executes correctly in the browser. When run in  **production mode**, bootstrap javascript does not execute (like the bootstrap javascript is not present). 

This page describes the differences between production and development modes 
https://medium.com/webpack/webpack-4-mode-and-optimization-5423a6bc597a. I will walk through these to see if I can get production to work. 

-----------
FOLLOW UP

I figured out the cause of the error. The test should have been more specific, `bootstrap.js` instead of `bootstrap` because there is also a `bootstrap.scss` which is imported in `bootstrap.js` so it appears to bork on instructions to include something twice. 

bootstrap: {
chunks: 'all',
name: 'bootstrap',
test: 'bootstrap.js',
enforce: true
}
```

EDIT: in my case the error was caused by my own mistake, fixed here: https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/pull/159

leaving my ravings here in case they are useful...

Moreover , everything works fine when I add { enforce: true } in my splitChunks entries or remove { chunks: 'all' } from them

My splitChunks looks like this:

splitChunks: {
  chunks: 'all',
  maxInitialRequests: Infinity,
  maxAsyncRequests: Infinity,
  maxSize: 200000,
},

I can fix most but not all of the errors by changing it like so:

splitChunks: {
  // chunks: 'all',
  maxInitialRequests: Infinity,
  maxAsyncRequests: Infinity,
  maxSize: Infinity,
  minSize: 0,
},

For me, removing chunks: 'all' solved some errors, and adding maxSize: Infinity and minSize: 0 solved more.

Obviously this is not a good solution because that means my app (with multiple entry points served on the same pages) will have an enormous amount of duplication.

This is still a problem, and for me renaming the chunks did NOT help (they aren't conflicting with entry points anyway). This is my cacheGroups entry:

          common: module => ({
            name: 'common',
            chunks: chunk => chunk.canBeInitial() && !/\.print$|^themes_/.test(chunk.name),
            // theme CSS files shouldn't be included in the
            // common.css chunk, otherwise they will interfere
            // with interface CSS
            minChunks: /styles\/themes/.test(module.request) ? 9999 : 2,
          }),

ERROR in chunk common [initial]
css/common.ac521d7b.css
Cannot read property 'pop' of undefined

When will this get resolved? I'm running into the same issue here.

When somebody send a PR

@evilebottnawi I think the problem here is that pretty much nobody actually knows what exactly the issue is and how to properly fix it!

Skipping the whole typeof chunkGroup.getModuleIndex2 === 'function' case works for me, but of course I don't want to maintain a fork of this package just for this...

Super ugly workaround to force the non-broken fallback logic without editing the code of this library:

class FixedMiniCssExtractPlugin extends MiniCssExtractPlugin {
  // This very awful workaround prevents a weird `<undefined>.pop()` in the plugin
  // that's caused by who-knows-what (NOT related to dynamic imports).
  // See this github issue for details:
  // https://github.com/webpack-contrib/mini-css-extract-plugin/issues/257
  renderContentAsset(compilation, chunk, modules, requestShortener) {
    const [chunkGroup] = chunk.groupsIterable;
    let rv;
    const getModuleIndex2 = chunkGroup.getModuleIndex2;
    try {
      chunkGroup.getModuleIndex2 = null;
      rv = super.renderContentAsset(compilation, chunk, modules, requestShortener);
    } finally {
      chunkGroup.getModuleIndex2 = getModuleIndex2;
    }
    return rv;
  }
}

Webpack is the biggest joke played on the JavaScript community so far.

Webpack is the biggest joke played on the JavaScript community so far.

As annoying as webpack issues can be, I don't think that comment helps anyone @stephan-v.

Somebody can create minimum reproducible test repo? Thanks

This issue is intermittent for me. I am unable to nail down exactly what changes will bring out this bug. I will go weeks without seeing it and then it plagues me for weeks. The workaround also works, but that doesn't play nice with CI testing which I would prefer not to patch the plugin with a hacky workaround.

I will continue to try and figure out what brings it out, and get a minimum repro case.

Failed with version 9.0.5, no problem with version 9.0.7-canary.3.

I have published a workaround for the version 0.5.0
https://github.com/ChoOo7/mini-css-extract-plugin/tree/0.5.0

why this issue closed, it is not solved.

Following up from research by @ndisidore and @cshomo11, I discovered that some chunks in the chunk group had modules that were not in the chunk group's _moduleIndices2 property, so I submitted a PR that tries to match each chunk's modules if that property doesn't return any matches.

Still an issue: Is there any fix?

ERROR in chunk layout.theme [initial]
layout.theme.css.liquid
Cannot read property 'pop' of undefined

I don't know why but add enforce: true does fix this for my vendors cache group.

This issue is still not solved entirely? I ran into this out of nowhere. XD
I had an system update yesterday and no idea what got updated.

Somebody still the problem? If yes can you try https://github.com/webpack-contrib/mini-css-extract-plugin/issues/341#issuecomment-675603278, I want to improve error reporting and close this issue.

Avoid using the entry point as the name for the cacheGroup in webpack@4
Fixed in webpack@5

Somebody still the problem? If yes can you try #341 (comment), I want to improve error reporting and close this issue.

using "enforce: true" is not an option for me, because it ignores the cacheGroup.minSize property and I need it.

Is there another way around this?

In my case this was resolved by changing the priority of the shared chunk. My chunking configuration is something similar to that used in Next.js

      {
        chunks: "all",
        maxInitialRequests: 25,
        minSize: 20000,
        cacheGroups: {
            default: false,
            vendors: false,
            framework: {
                chunks: "all",
                name: "framework",
                // https://github.com/vercel/next.js/pull/9012
                test: /(?<!node_modules.*)[\\/]node_modules[\\/](react|react-dom|scheduler|prop-types|use-subscription|react-redux|rxjs)[\\/]/,
                priority: 40,
                enforce: true
            },
            lib: {
                test(module) {
                    return module.size() > 20000 && /node_modules[/\\]/.test(module.identifier());
                },
                name(module) {
                    const hash = crypto.createHash("sha1");
                    if (isModuleCSS(module)) {
                        module.updateHash(hash);
                    } else {
                        if (!module.libIdent) {
                            throw new Error(`Encountered unknown module type: ${module.type}.`);
                        }

                        hash.update(module.libIdent({context: outputDir}));
                    }

                    return hash.digest("hex").substring(0, 8);
                },
                priority: 30,
                minChunks: 1,
                reuseExistingChunk: true
            },
            commons: {
                chunks: "all",
                // if a chunk is used more than half the routes it may be assumed common
                minChunks: NUMBER_OF_ASYNC_ROUTES > 2 ? NUMBER_OF_ASYNC_ROUTES * 0.5 : 2,
                priority: 20
            },
            shared: {
                name(module, chunks) {
                    return (
                        crypto
                            .createHash("sha1")
                            .update(
                                chunks.reduce((acc, chunk) => {
                                    return acc + chunk.name;
                                }, "")
                            )
                            .digest("hex") + (isModuleCSS(module) ? "_CSS" : "")
                    );
                },
                priority: 10,
                minChunks: 2,
                reuseExistingChunk: true
            }
        }

Changing the priority of the chunk shared from priority: 10 to priority: 35 fixed the issue.

When checking the output bundles using webpack-bundle-analyzer I had noticed that a vendor CSS file was being included multiple times in lazy loaded chunks instead of only being included in the shared chunk.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mike-marcacci picture mike-marcacci  ·  4Comments

Legends picture Legends  ·  4Comments

BODhaha picture BODhaha  ·  3Comments

stavalfi picture stavalfi  ·  4Comments

skrobek picture skrobek  ·  4Comments