{"record":{"id":"07fc55faacd21672","repo":"netbirdio/netbird","slug":"user-groups-cannot-be-empty","errorCode":null,"errorMessage":"user groups cannot be empty","messagePattern":"user groups cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/cmd/expose.go","lineNumber":131,"sourceCode":"\n\tif isClusterProtocol(exposeProtocol) {\n\t\tif exposePin != \"\" || exposePassword != \"\" || len(exposeUserGroups) > 0 {\n\t\t\treturn 0, fmt.Errorf(\"auth flags (--with-pin, --with-password, --with-user-groups) are not supported for %s protocol\", exposeProtocol)\n\t\t}\n\t} else if cmd.Flags().Changed(\"with-external-port\") {\n\t\treturn 0, fmt.Errorf(\"--with-external-port is not supported for %s protocol\", exposeProtocol)\n\t}\n\n\tif exposePin != \"\" && !pinRegexp.MatchString(exposePin) {\n\t\treturn 0, fmt.Errorf(\"invalid pin: must be exactly 6 digits\")\n\t}\n\n\tif cmd.Flags().Changed(\"with-password\") && exposePassword == \"\" {\n\t\treturn 0, fmt.Errorf(\"password cannot be empty\")\n\t}\n\n\tif cmd.Flags().Changed(\"with-user-groups\") && len(exposeUserGroups) == 0 {\n\t\treturn 0, fmt.Errorf(\"user groups cannot be empty\")\n\t}\n\n\treturn port, nil\n}\n\nfunc isProtocolValid(exposeProtocol string) bool {\n\tswitch strings.ToLower(exposeProtocol) {\n\tcase \"http\", \"https\", \"tcp\", \"udp\", \"tls\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc exposeFn(cmd *cobra.Command, args []string) error {\n\tSetFlagsFromEnvVars(rootCmd)\n\n\tif err := util.InitLog(logLevel, util.LogConsole); err != nil {","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/cmd/expose.go#L113-L149","documentation":"--with-user-groups was explicitly set but resolved to an empty list: cmd.Flags().Changed(\"with-user-groups\") is true while len(exposeUserGroups) == 0. The flag is a StringSlice, and an explicitly empty string parses into zero entries, so this catches `--with-user-groups=` and `--with-user-groups \"\"`.","triggerScenarios":"`--with-user-groups=` with an empty value, typically an unset or empty groups variable expanded by a wrapper script.","commonSituations":"Automation passing an optional groups variable that is empty in some environments; command built by string concatenation with an empty tail.","solutions":["Pass at least one group: `--with-user-groups devops,Backend`","Build the command conditionally: only append the flag when the list is non-empty (`[ -n \"$GROUPS\" ] && set -- \"$@\" --with-user-groups \"$GROUPS\"`)"],"exampleFix":"# before\nnetbird expose --with-user-groups \"$GROUPS\" 8080   # GROUPS empty\n\n# after\nexport GROUPS=devops,Backend\nnetbird expose --with-user-groups \"$GROUPS\" 8080","handlingStrategy":"validation","validationCode":"if groupsFlagSet && len(groups) == 0 {\n\tlog.Fatal(\"--with-user-groups was set but the list is empty; pass at least one group or drop the flag\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build the command conditionally: append --with-user-groups only when the list is non-empty","StringSlice flags parse an explicit empty string as zero entries — unset and empty are different failure modes","Echo the variable before using it when debugging wrapper scripts"],"tags":["cli","validation","user-groups","empty-value","netbird-cli"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}