gohugoio/hugo · error

version name is invalid

Error message

version name %q is invalid: %s

What it means

During versions config initialization, each version name key is validated with paths.ValidateIdentifier (versions.go:167-168), which requires the name to equal its normalized form — effectively all lowercase, no spaces, and no characters Hugo's path normalization would alter. A version name that fails (e.g. uppercase letters, spaces, or special characters) produces this error with the underlying reason.

Solutions

  1. Lowercase the version name and remove spaces (e.g. 'V1.0' -> 'v1.0', 'My Version' -> 'myversion').
  2. Use dots/dashes which ValidateIdentifier permits: 'v1.0.0', 'v1-0-0'.
  3. Keep version keys stable across content that references them (aliases/redirects) after renaming.

Example fix

// before (config)
[versions]
"V1.2.0"

// after
[versions]
"v1.2.0"
Defensive patterns

Strategy: validation

Validate before calling

func normalizeVersionName(name string) (string, error) {
    if name == "" {
        return "", errors.New("version name cannot be empty")
    }
    norm := strings.ToLower(strings.ReplaceAll(name, " ", ""))
    if err := paths.ValidateIdentifier(norm); err != nil {
        return "", fmt.Errorf("version name %q is invalid: %w", name, err)
    }
    return norm, nil
}

Type guard

func isValidVersionName(name string) bool {
    return name != "" && name == paths.NormalizePathStringBasic(name)
}

Prevention

When it happens

Trigger: Defining a [versions] config block with keys like 'V1.0', 'My Version', or 'v 1.0' — any key that is not already lowercase-alphanumeric-friendly triggers ValidateIdentifier failure ('must be all lower case and no spaces').

Common situations: Using SemVer-looking but uppercase version tags; copying version names from release notes that include spaces; renaming a version in config without normalizing.

Related errors


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

Appendix: source

Thrown at hugolib/versions/versions.go:168

	if r.versionConfigs == nil {
		r.versionConfigs = make(map[string]VersionConfig)
	}
	defaultContentVersionProvided := defaultContentVersion != ""
	if len(r.versionConfigs) == 0 {
		if defaultContentVersion == "" {
			defaultContentVersion = defaultContentVersionFallback
		}
		// Add a default version.
		r.versionConfigs[defaultContentVersion] = VersionConfig{}
	}

	var defaultSeen bool
	for k, v := range r.versionConfigs {
		if k == "" {
			return errors.New("version name cannot be empty")
		}
		if err := paths.ValidateIdentifier(k); err != nil {
			return fmt.Errorf("version name %q is invalid: %s", k, err)
		}

		var isDefault bool
		if k == defaultContentVersion {
			isDefault = true
			defaultSeen = true
		}

		// Ignore error, this may not be a semver version.
		parsedVersion, _ := version.ParseVersion(k)

		r.Sorted = append(r.Sorted, VersionInternal{Name: k, parsedVersion: parsedVersion, Default: isDefault, VersionConfig: v})
	}

	// Sort by weight if set, then semver descending.
	sort.SliceStable(r.Sorted, func(i, j int) bool {
		ri, rj := r.Sorted[i], r.Sorted[j]
		if ri.Weight == rj.Weight {

View on GitHub (pinned to 52c9bd7908)