netbirdio/netbird · error
user group name cannot be empty
Error message
user group name cannot be empty
What it means
Returned by ExposeServiceRequest.Validate when the user_groups slice of a peer-expose request contains an empty string. User groups name the distribution groups used for bearer (SSO) token distribution, so an empty name cannot map to any group. Empty entries are almost always client-side parsing artifacts, not intentional input.
Source
Thrown at management/internals/modules/reverseproxy/service/service.go:1499
switch r.Mode {
case ModeHTTP, ModeTCP, ModeUDP, ModeTLS:
default:
return fmt.Errorf("unsupported mode %q", r.Mode)
}
if IsL4Protocol(r.Mode) {
if r.Pin != "" || r.Password != "" || len(r.UserGroups) > 0 {
return fmt.Errorf("authentication is not supported for %s mode", r.Mode)
}
}
if r.Pin != "" && !pinRegexp.MatchString(r.Pin) {
return errors.New("invalid pin: must be exactly 6 digits")
}
for _, g := range r.UserGroups {
if g == "" {
return errors.New("user group name cannot be empty")
}
}
if r.NamePrefix != "" && !validNamePrefix.MatchString(r.NamePrefix) {
return fmt.Errorf("invalid name prefix %q: must be lowercase alphanumeric with optional hyphens, 1-32 characters", r.NamePrefix)
}
return nil
}
// ToService builds a Service from the expose request.
func (r *ExposeServiceRequest) ToService(accountID, peerID, serviceName string) *Service {
svc := &Service{
AccountID: accountID,
Name: serviceName,
Mode: r.Mode,
Enabled: true,
}View on GitHub (pinned to 93e97f4bf1)
Solutions
- Filter out empty entries before sending: only non-empty group names in user_groups.
- Trim whitespace from each entry when parsing user input, then drop empties.
- Verify each name matches an existing distribution group in the account so the bearer config applies.
Example fix
// before
groups := strings.Split("admins,", ",") // ["admins", ""]
req := ExposeServiceRequest{ Mode: "http", Port: 8080, UserGroups: groups }
// after
var groups []string
for _, g := range strings.Split(raw, ",") {
if g = strings.TrimSpace(g); g != "" {
groups = append(groups, g)
}
}
req := ExposeServiceRequest{ Mode: "http", Port: 8080, UserGroups: groups } Defensive patterns
Strategy: validation
Validate before calling
func cleanUserGroups(raw []string) ([]string, error) {
var out []string
for _, g := range raw {
g = strings.TrimSpace(g)
if g == "" {
return nil, errors.New("user group name cannot be empty")
}
out = append(out, g)
}
return out, nil
} Type guard
func hasNoEmptyUserGroups(groups []string) bool {
for _, g := range groups {
if strings.TrimSpace(g) == "" {
return false
}
}
return true
} Try / catch
if err := req.Validate(); err != nil {
if strings.Contains(err.Error(), "user group name cannot be empty") {
return respondBadRequest(errors.New("filter empty entries from user_groups (trailing comma?)"))
}
return respondBadRequest(err)
} Prevention
- Trim and filter list inputs before submitting - strings.Split on a trailing comma yields an empty element.
- Validate group names against the account's actual distribution groups before the request.
- Use JSON schemas with minLength: 1 on array items for user-facing forms.
When it happens
Trigger: Building user_groups with strings.Split(input, ",") where the input has a trailing comma ("admins,,devs" or "admins,"); serializing [""] from a form field that was left blank; trimming nothing so " " passes but a true empty string fails.
Common situations: CLI flags parsed as comma-separated lists without filtering empties. JSON payloads constructed by joining optional fields where one was absent. Copy-paste between requests where a group placeholder was never filled in.
Related errors
- private services cannot enable bearer auth (SSO): NetBird-on
- invalid pin: must be exactly 6 digits
- service name is required
- service name exceeds maximum length of 255 characters
- at least one target is required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/cecc873ec0ba0a14.
Report an issue: GitHub.