gohugoio/hugo · error
role name cannot be empty
Error message
role name cannot be empty
What it means
Thrown by RolesInternal.init in hugolib/roles/roles.go:150 when iterating the roleConfigs map and a key is the empty string. Hugo's content-roles feature (multi-audience rendering) keys each role by name, so an empty key is rejected before sorting/validation.
Solutions
- Inspect the [roles] section of hugo.toml and remove or rename any [roles.""] entry.
- Give every role a non-empty identifier, e.g. [roles.member] and [roles.admin].
- Run hugo config to print the resolved roles map and locate the empty key.
Example fix
// before [roles] [roles.""] weight = 1 // after [roles.member] weight = 1
Defensive patterns
Strategy: validation
Validate before calling
// Before building config, reject empty role keys.
for k := range roleConfigs {
if k == "" { return errors.New("remove empty role key") }
} Prevention
- Validate the [roles] table with hugo config before building.
- Never emit a [roles.""] entry from config generators.
- Treat an empty role key as a config-generation bug.
When it happens
Trigger: Configuring [roles] with an empty-key sub-table in TOML (e.g. [roles.""]), or programmatically building a roles config map containing the "" key. The init runs during site matrix construction in site.go.
Common situations: Malformed TOML/YAML config under [roles], a copy-paste that produced an empty quoted key, or a config generator that emits a blank role entry. A default role ("guest") is added automatically when no roles are configured, so this only fires on an explicit empty key.
Related errors
- config needs a filename
- empty related config provided
- failed to decode languages config
- failed to decode options for resource
- failed to decode related config
AI-assisted analysis of gohugoio/hugo@52c9bd7908 (2026-08-09).
Data as JSON: /api/errors/6783f5c0eeedfe71.
Report an issue: GitHub.
Appendix: source
Thrown at hugolib/roles/roles.go:150
const defaultContentRoleFallback = "guest"
func (r *RolesInternal) init(defaultContentRole string) (string, error) {
if r.roleConfigs == nil {
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]View on GitHub (pinned to 52c9bd7908)