gohugoio/hugo · error

cannot create " ": Windows filename restriction

Error message

cannot create "%s": Windows filename restriction

What it means

Returned by targetPathAlias only on Windows (runtime.GOOS == "windows") when the alias fails one or more Windows filename checks: reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9), forbidden characters (: * ? \" < > |), ASCII control chars (0x00-0x1F), or components with trailing space/period. The individual problems are logged via Errorln first, then the aggregate error is returned. On non-Windows the same problems are only logged as Info and the alias proceeds.

Solutions

  1. Rename the alias to avoid reserved device names and the chars : * ? \" < > | and trailing space/period.
  2. Move the build off Windows for affected aliases, or pre-process aliases to sanitize Windows-illegal components.
  3. Add a pre-build lint that rejects Windows-invalid alias segments regardless of build OS.

Example fix

---
# before (fails on Windows)
aliases:
  - "help:nlp"
# after
aliases:
  - "help-nlp"
---
Defensive patterns

Strategy: validation

Validate before calling

var reserved = []string{"CON","PRN","AUX","NUL","COM0","COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","LPT0","LPT1","LPT2","LPT3","LPT4","LPT5","LPT6","LPT7","LPT8","LPT9"}
func windowsSafeAlias(a string) error {
    if strings.ContainsAny(a, `:*?\"<>|`) { return fmt.Errorf("bad char") }
    for _, c := range strings.Split(path.Clean(filepath.ToSlash(a)), "/") {
        for _, r := range reserved { if c == r { return fmt.Errorf("reserved") } }
        if strings.TrimRight(c, " .") != c { return fmt.Errorf("trailing space/dot") }
    }
    return nil
}

Prevention

When it happens

Trigger: Building on Windows with a front-matter alias containing a Windows-reserved name or forbidden char, e.g. aliases: ["aux"], aliases: ["a:b"], aliases: ["file."] — the publish step then aborts for that alias. Cross-platform teams hit it when a Linux/macOS-authored alias is fine there but fails on a Windows CI/colleague.

Common situations: Aliases with colons (common in some URL schemes), trailing dots, reserved device names, or question marks; content authored on case-sensitive OSes reaching a Windows builder; CI matrix that includes windows-latest.

Related errors


AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09). Data as JSON: /api/errors/63ceb98919cf0f83. Report an issue: GitHub.

Appendix: 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), nil

View on GitHub (pinned to 52c9bd7908)