gohugoio/hugo · error
failed to resolve CSS @import "%s"; %s
Error message
failed to resolve CSS @import "%s"; %s
What it means
With `inlineImports = true`, Hugo's PostCSS/CSS transformer rewrites `@import` statements by inlining the imported file's content before invoking postcss. If the imported file can't be read from Hugo's asset filesystem, and `skipInlineImportsNotFound` is not enabled, the transform fails with a file-positioned error pointing at the offending @import line. Imports living in node_modules are the classic case, since they aren't in Hugo's assets mounts.
Source
Thrown at resources/resource_transformers/cssjs/inline_imports.go:122
trackLine(i, offset, line)
} else {
path := strings.Trim(strings.TrimPrefix(line, importIdentifier), " \"';")
filename := filepath.Join(basePath, path)
imp.dependencyManager.AddIdentity(identity.CleanStringIdentity(filename))
importContent, hash := imp.contentHash(filename)
if importContent == nil {
if imp.opts.SkipInlineImportsNotFound {
trackLine(i, offset, line)
continue
}
pos := text.Position{
Filename: inPath,
LineNumber: offset + 1,
ColumnNumber: column + 1,
}
msgDetail := "if this import's source lives in node_modules, enable the skipInlineImportsNotFound option, see https://gohugo.io/functions/css/postcss/#skipinlineimportsnotfound"
return 0, "", herrors.NewFileErrorFromFileInPos(fmt.Errorf("failed to resolve CSS @import \"%s\"; %s", filename, msgDetail), pos, imp.fs, nil)
}
i--
if imp.contentSeen[hash] {
i++
// Just replace the line with an empty string.
replacements = append(replacements, []string{line, ""}...)
trackLine(i, offset, "IMPORT")
continue
}
imp.contentSeen[hash] = true
// Handle recursive imports.
l, nested, err := imp.importRecursive(i+lineNum, string(importContent), filepath.ToSlash(filename))
if err != nil {
return 0, "", errView on GitHub ↗ (pinned to 8a468df065)
Solutions
- If the import's source lives in node_modules, set `skipInlineImportsNotFound = true` in the PostCSS options so Hugo leaves it for postcss-import to resolve (see https://gohugo.io/functions/css/postcss/#skipinlineimportsnotfound).
- Fix the @import path so it resolves relative to the importing file within assets.
- Mount the needed directory (e.g. node_modules package) into assets via module mounts so Hugo can inline it.
Example fix
// before (template)
{{ $css := $styles | css.PostCSS (dict "inlineImports" true) }}
// after
{{ $css := $styles | css.PostCSS (dict "inlineImports" true "skipInlineImportsNotFound" true) }} Defensive patterns
Strategy: validation
Validate before calling
// before enabling inlineImports = true, verify every @import target exists in assets/ // grep -o '@import "[^"]*"' assets/css/main.css and check each file is mounted
Try / catch
opts := map[string]any{"inlineImports": true, "skipInlineImportsNotFound": true} // only if missing imports are acceptable Prevention
- Keep all @import-ed CSS files under assets/ (or a mounted module dir) with paths relative to the importing file
- Use `skipInlineImportsNotFound = true` deliberately for vendor URLs you can't inline, not as a blanket suppressor
- Remember inlineImports only handles plain `@import "...";` — no media queries or url() forms
When it happens
Trigger: `css.PostCSS (dict "inlineImports" true)` on a CSS file containing `@import "foo.css";` where foo.css does not exist relative to the importing file within Hugo's mounted assets; imports resolved by postcss-import from node_modules that Hugo's inliner can't see.
Common situations: Importing npm-package CSS (e.g. `@import "normalize.css";`) without mounting node_modules into assets; renamed or moved partial CSS files; path case mismatches on Linux CI vs macOS dev.
Related errors
- must not provide more arguments than resource object and opt
- postcss config %q not found
- local ref %q not found
- targetPath cannot be empty
- unsupported transpiler %q; valid values are %q or %q
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/8e6ede53fdb83973.json.
Report an issue: GitHub ↗.