Webpack css-loader "Can't resolve '<asset>'" in url() - Fix in CI
By default css-loader resolves url() references in stylesheets as module requests, like JS imports. A url() pointing at a missing file, a wrong/miscased path, or a host-absolute URL that should not be resolved makes the build fail with "Can't resolve".
What this error means
The build fails with Module not found: Error: Can't resolve './images/bg.png' originating from a CSS file's url(). It is deterministic and names the stylesheet and the referenced asset.
ERROR in ./src/styles/main.css (./node_modules/css-loader/dist/cjs.js!./src/styles/main.css)
Module not found: Error: Can't resolve './images/Bg.png' in '/app/src/styles'
@ ./src/styles/main.css (url(./images/Bg.png))Common causes
Asset path wrong or miscased
The url() points at a file that does not exist at that path, or its casing differs (Bg.png vs bg.png). Case-sensitive Linux CI fails where macOS resolved it.
A url() that should not be resolved
A root-relative (/assets/x.png served at runtime) or external URL is being treated as a module request, so Webpack tries and fails to resolve it at build time.
How to fix it
Fix the asset path and case
Point url() at the real file with exact casing, or move the asset so the relative path resolves.
ls -la src/styles/images/ # confirm bg.png vs Bg.png
/* main.css */
.hero { background: url('./images/bg.png'); }Skip resolution for runtime-served URLs
Tell css-loader not to resolve root-relative/external URLs it should leave untouched.
// webpack.config.js
{
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { url: { filter: (u) => !u.startsWith('/') } } },
],
}How to prevent it
- Match
url()asset paths and casing to real files. - Filter runtime-served URLs out of css-loader resolution.
- Build CSS in CI so url() resolution gaps fail before deploy.