{"record":{"id":"e38ea7e2348411dd","repo":"juanfont/headscale","slug":"w-group-q-expected-string-but-got-t","errorCode":null,"errorMessage":"%w: group %q expected string but got %T","messagePattern":"%w: group %q expected string but got %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":1346,"sourceCode":"\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// Then validate each field can be converted to []string\n\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}","sourceCodeStart":1328,"sourceCodeEnd":1364,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L1328-L1364","documentation":"While unmarshalling Groups, a member of a group array is not a JSON string (ErrInvalidGroupMember). The message names the group key and the Go type of the bad element.","triggerScenarios":"A groups value containing a number, object, boolean, or null element, e.g. {\"group:x\": [42]} or {\"group:x\": [null]}.","commonSituations":"Hand-editing JSON (not HuJSON) policy and forgetting quotes, or a template/generator emitting unquoted values.","solutions":["Quote every group member so it is a JSON string","If generating policy, ensure member values are serialized as strings"],"exampleFix":"// before\n{\"group:eng\": [42]}\n// after\n{\"group:eng\": [\"42@example.com\"]}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func allMembersStrings(v []any) bool {\n\tfor _, m := range v { if _, ok := m.(string); !ok { return false } }\n\treturn true\n}","tryCatchPattern":null,"preventionTips":["Quote all group members in hand-written JSON","Validate generated policy against a JSON schema before submitting"],"tags":["policy","groups","json","type-error"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}