gohugoio/hugo · error
role name is invalid
Error message
role name %q is invalid: %s
What it means
Raised by RolesInternal.init (roles.go:154) when a configured role name fails paths.ValidateIdentifier, which requires the string to equal its NormalizePathStringBasic form — i.e. all lowercase ASCII, no spaces, no slashes, no punctuation outside the basic path character set.
Solutions
- Use lowercase, hyphen-separated, space-free role identifiers in config (e.g. `editor`, `guest`, `en-us`).
- If you need a display name, store it separately and keep the key as a slug.
Example fix
# before (hugo.toml)
[roles]
[roles."Editor Role"]
weight = 1
# after
[roles]
[roles.editor]
weight = 1 Defensive patterns
Strategy: validation
Validate before calling
# Validate every role key is lowercase, no spaces.
awk '/^\[roles\./ {
name=$0; gsub(/\[roles\.|\]/,"",name);
if (name != tolower(name) || name ~ / /) print "bad role: " name
}' hugo.toml Prevention
- Use lowercase, hyphen-only role keys (slugs).
- Treat role keys as machine identifiers; store display names separately.
- Add the awk check above to CI for hugo.toml.
When it happens
Trigger: Declaring a role under `roles` in config with uppercase letters, spaces, or special characters (e.g. `Editor Role`, `Editor`, `en-US`). The validator runs on every role-config key during init.
Common situations: Defining content roles for a multi-role Hugo setup using friendly display names instead of slugs; copy-pasting role names from a UI label field; locale-style names with hyphens or uppercase region codes.
Related errors
- the configured defaultContentRole
- cardinalityThreshold threshold must be between 0 and 100
- failed to compile Glob
- failed to decode segments
- markup must not be set, use mediaType
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/ae584f36bc2802db.
Report an issue: GitHub.
Appendix: source
Thrown at hugolib/roles/roles.go:154
r.roleConfigs = make(map[string]RoleConfig)
}
defaultContentRoleProvided := defaultContentRole != ""
if len(r.roleConfigs) == 0 {
// Add a default role.
if defaultContentRole == "" {
defaultContentRole = defaultContentRoleFallback
}
r.roleConfigs[defaultContentRole] = RoleConfig{}
}
var defaultSeen bool
for k, v := range r.roleConfigs {
if k == "" {
return "", errors.New("role name cannot be empty")
}
if err := paths.ValidateIdentifier(k); err != nil {
return "", fmt.Errorf("role name %q is invalid: %s", k, err)
}
var isDefault bool
if k == defaultContentRole {
isDefault = true
defaultSeen = true
}
r.Sorted = append(r.Sorted, RoleInternal{Name: k, Default: isDefault, RoleConfig: v})
}
// Sort by weight if set, then by name.
sort.SliceStable(r.Sorted, func(i, j int) bool {
ri, rj := r.Sorted[i], r.Sorted[j]
if ri.Weight == rj.Weight {
return ri.Name < rj.Name
}
if rj.Weight == 0 {View on GitHub (pinned to 52c9bd7908)