{"record":{"id":"fe9bede26574a358","repo":"juanfont/headscale","slug":"w-group-q-got-string-q","errorCode":null,"errorMessage":"%w: group %q got string: %q","messagePattern":"%w: group %q got string: %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":1352,"sourceCode":"\trawGroups := make(map[string][]string)\n\n\tfor key, value := range rawMap {\n\t\tswitch v := value.(type) {\n\t\tcase []any:\n\t\t\t// Convert []interface{} to []string\n\t\t\tvar stringSlice []string\n\n\t\t\tfor _, item := range v {\n\t\t\t\tif str, ok := item.(string); ok {\n\t\t\t\t\tstringSlice = append(stringSlice, str)\n\t\t\t\t} else {\n\t\t\t\t\treturn fmt.Errorf(\"%w: group %q expected string but got %T\", ErrInvalidGroupMember, key, item)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trawGroups[key] = stringSlice\n\t\tcase string:\n\t\t\treturn fmt.Errorf(\"%w: group %q got string: %q\", ErrGroupValueNotArray, key, v)\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"%w: group %q got %T\", ErrGroupValueNotArray, key, v)\n\t\t}\n\t}\n\n\t// Reject group-in-group references. Reverse-sort the keys so the\n\t// reported (parent, child) pair names the deepest non-leaf parent\n\t// first.\n\tkeys := make([]string, 0, len(rawGroups))\n\tfor k := range rawGroups {\n\t\tkeys = append(keys, k)\n\t}\n\n\tslices.Sort(keys)\n\tslices.Reverse(keys)\n\n\tfor _, key := range keys {\n\t\tfor _, u := range rawGroups[key] {","sourceCodeStart":1334,"sourceCodeEnd":1370,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L1334-L1370","documentation":"A groups entry's value is a bare JSON string instead of an array of strings (ErrGroupValueNotArray). The message shows the offending string.","triggerScenarios":"Writing \"group:x\": \"user@example.com\" instead of [\"user@example.com\"] in the groups map.","commonSituations":"Single-member groups written without brackets; converting YAML-ish config to HuJSON/JSON and dropping the list syntax.","solutions":["Wrap the value in square brackets to make it an array","Run headscale policy check (or equivalent validation) before applying"],"exampleFix":"// before\n{\"group:eng\": \"dev@example.com\"}\n// after\n{\"group:eng\": [\"dev@example.com\"]}","handlingStrategy":"validation","validationCode":"func groupValueIsArray(raw map[string]any) bool {\n\tfor _, v := range raw { if _, ok := v.([]any); !ok { return false } }\n\treturn true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always bracket group values, even single-member groups"],"tags":["policy","groups","json","shape-error"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}