gohugoio/hugo · error
cannot create "%s": Windows filename restriction
Error message
cannot create "%s": Windows filename restriction
What it means
Hugo validates alias paths against Windows filename rules (reserved device names like CON/PRN/AUX/NUL/COM1-9/LPT1-9, characters `: * ? " < > |`, ASCII control codes, and components ending in a space or period). On non-Windows hosts these problems are only logged at info level, but when building on Windows (runtime.GOOS == "windows") the file genuinely cannot be created, so each violation is logged as an error and the build fails with this message.
Source
Thrown at hugolib/alias.go:172
continue
}
}
for _, comp := range components {
if strings.HasSuffix(comp, " ") || strings.HasSuffix(comp, ".") {
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with a trailing space or period, problematic on Windows", originalAlias))
}
for _, r := range reservedNames {
if comp == r {
msgs = append(msgs, fmt.Sprintf("Alias \"%s\" contains component with reserved name \"%s\" on Windows", originalAlias, r))
}
}
}
if len(msgs) > 0 {
if runtime.GOOS == "windows" {
for _, m := range msgs {
a.log.Errorln(m)
}
return "", fmt.Errorf("cannot create \"%s\": Windows filename restriction", originalAlias)
}
for _, m := range msgs {
a.log.Infoln(m)
}
}
// Add the final touch. When the alias does not already end in one of the
// output format's configured suffixes, treat it as a directory and append
// the format's base name and suffix (e.g. index.html).
alias = strings.TrimPrefix(alias, "/")
baseFile := of.BaseName + of.MediaType.FirstSuffix.FullSuffix
if strings.HasSuffix(alias, "/") {
alias = alias + baseFile
} else if !pathHasOutputFormatSuffix(alias, of) {
alias = alias + "/" + baseFile
}
return filepath.FromSlash(alias), nilView on GitHub ↗ (pinned to 8a468df065)
Solutions
- Read the preceding error lines to identify the exact offending alias and violation, then rename that alias component (e.g. `aux` → `auxiliary`, drop `?`/`:` characters, remove trailing dots/spaces).
- Strip query strings from aliases — an alias is a path, not a full URL; `/page?id=3` should become a plain path or be handled by server-side redirects.
- If the legacy URL genuinely requires forbidden characters, handle that redirect at the web server/CDN layer instead of a Hugo alias.
- As a workaround, build on a non-Windows machine or WSL, where these are downgraded to info-level warnings (but the published files may still be unusable on Windows filesystems).
Example fix
# before --- aliases: ["/docs/aux/setup", "/faq?old"] --- # after --- aliases: ["/docs/auxiliary/setup", "/faq-old"] ---
Defensive patterns
Strategy: validation
Validate before calling
var winReserved = regexp.MustCompile(`(?i)^(con|prn|aux|nul|com[1-9]|lpt[1-9])($|\.)`)
func winSafeAlias(a string) bool {
for _, seg := range strings.Split(path.Clean(a), "/") {
if winReserved.MatchString(seg) || strings.ContainsAny(seg, `<>:"|?*`) {
return false
}
}
return true
} Try / catch
if err != nil && strings.Contains(err.Error(), "Windows filename restriction") {
// rename the alias segment (e.g. "con" -> "console") and rebuild
} Prevention
- Avoid Windows-reserved names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) as path segments in aliases and slugs
- Run CI builds on Windows (or with a reserved-name lint) if any contributor builds on Windows
- Prefer lowercase alphanumeric-plus-dash slugs; forbid `<>:"|?*` characters
When it happens
Trigger: Building on Windows with a page whose `aliases` entry contains a reserved name component (e.g. `aliases: ["/docs/aux/setup"]`), a forbidden character (`aliases: ["/faq?old"]`, `"/a:b"`), a control character, or a component with a trailing dot or space. The specific violations are printed as separate error lines just before this failure.
Common situations: Cross-platform teams where a site builds fine on Linux/macOS CI but fails for a contributor on Windows; aliases copied from old URLs containing query strings (`?`) or colons; directory names like `con`, `nul`, or `lpt1` appearing innocently in migrated URL structures.
Related errors
- failed to save file %q:: %w
- failed to resolve output path %q: %w
- failed to copy %q to %q: %w
- target path "%s" exists but is not a directory
- failed to save file %q: %s
AI-assisted analysis of gohugoio/hugo@8a468df065 (2026-07-31).
Data as JSON: /data/errors/63ceb98919cf0f83.json.
Report an issue: GitHub ↗.