{"id":"63ceb98919cf0f83","repo":"gohugoio/hugo","slug":"cannot-create-s-windows-filename-restriction","errorCode":null,"errorMessage":"cannot create \"%s\": Windows filename restriction","messagePattern":"cannot create \"(.+?)\": Windows filename restriction","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hugolib/alias.go","lineNumber":172,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t}\n\tfor _, comp := range components {\n\t\tif strings.HasSuffix(comp, \" \") || strings.HasSuffix(comp, \".\") {\n\t\t\tmsgs = append(msgs, fmt.Sprintf(\"Alias \\\"%s\\\" contains component with a trailing space or period, problematic on Windows\", originalAlias))\n\t\t}\n\t\tfor _, r := range reservedNames {\n\t\t\tif comp == r {\n\t\t\t\tmsgs = append(msgs, fmt.Sprintf(\"Alias \\\"%s\\\" contains component with reserved name \\\"%s\\\" on Windows\", originalAlias, r))\n\t\t\t}\n\t\t}\n\t}\n\tif len(msgs) > 0 {\n\t\tif runtime.GOOS == \"windows\" {\n\t\t\tfor _, m := range msgs {\n\t\t\t\ta.log.Errorln(m)\n\t\t\t}\n\t\t\treturn \"\", fmt.Errorf(\"cannot create \\\"%s\\\": Windows filename restriction\", originalAlias)\n\t\t}\n\t\tfor _, m := range msgs {\n\t\t\ta.log.Infoln(m)\n\t\t}\n\t}\n\n\t// Add the final touch. When the alias does not already end in one of the\n\t// output format's configured suffixes, treat it as a directory and append\n\t// the format's base name and suffix (e.g. index.html).\n\talias = strings.TrimPrefix(alias, \"/\")\n\tbaseFile := of.BaseName + of.MediaType.FirstSuffix.FullSuffix\n\tif strings.HasSuffix(alias, \"/\") {\n\t\talias = alias + baseFile\n\t} else if !pathHasOutputFormatSuffix(alias, of) {\n\t\talias = alias + \"/\" + baseFile\n\t}\n\n\treturn filepath.FromSlash(alias), nil","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/gohugoio/hugo/blob/8a468df065a75c1c7cf9f6850f32148746590ea5/hugolib/alias.go#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\n---\naliases: [\"/docs/aux/setup\", \"/faq?old\"]\n---\n\n# after\n---\naliases: [\"/docs/auxiliary/setup\", \"/faq-old\"]\n---","handlingStrategy":"validation","validationCode":"var winReserved = regexp.MustCompile(`(?i)^(con|prn|aux|nul|com[1-9]|lpt[1-9])($|\\.)`)\nfunc winSafeAlias(a string) bool {\n    for _, seg := range strings.Split(path.Clean(a), \"/\") {\n        if winReserved.MatchString(seg) || strings.ContainsAny(seg, `<>:\"|?*`) {\n            return false\n        }\n    }\n    return true\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"Windows filename restriction\") {\n    // rename the alias segment (e.g. \"con\" -> \"console\") and rebuild\n}","preventionTips":["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"],"tags":["hugo","aliases","windows","filesystem","cross-platform"],"analyzedSha":"8a468df065a75c1c7cf9f6850f32148746590ea5","analyzedAt":"2026-07-31T21:23:07.045Z","schemaVersion":2}