{"record":{"id":"734600bf1158ab1d","repo":"juanfont/headscale","slug":"type-not-supported","errorCode":null,"errorMessage":"type not supported","messagePattern":"type not supported","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hscontrol/policy/v2/types.go","lineNumber":119,"sourceCode":"// point operators at the issue.\nvar nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{\n\ttailcfg.NodeAttrFunnel: \"https://github.com/juanfont/headscale/issues/2527\",\n}\n\n// Policy validation errors.\nvar (\n\tErrInvalidUsername             = errors.New(\"username must contain @\")\n\tErrUserNotFound                = errors.New(\"user not found\")\n\tErrMultipleUsersFound          = errors.New(\"multiple users found\")\n\tErrInvalidGroupFormat          = errors.New(\"group must start with 'group:'\")\n\tErrInvalidTagFormat            = errors.New(\"tag must start with 'tag:'\")\n\tErrInvalidHostname             = errors.New(\"invalid hostname\")\n\tErrHostResolve                 = errors.New(\"error resolving host\")\n\tErrInvalidPrefix               = errors.New(\"invalid prefix\")\n\tErrInvalidAutogroup            = errors.New(\"invalid autogroup\")\n\tErrUnknownAutogroup            = errors.New(\"unknown autogroup\")\n\tErrHostportMissingColon        = errors.New(\"hostport must contain a colon\")\n\tErrTypeNotSupported            = errors.New(\"type not supported\")\n\tErrInvalidAlias                = errors.New(\"invalid alias format\")\n\tErrInvalidAutoApprover         = errors.New(\"invalid auto approver format\")\n\tErrInvalidOwner                = errors.New(\"invalid owner format\")\n\tErrGroupNotDefined             = errors.New(\"group not defined in policy\")\n\tErrInvalidGroupMember          = errors.New(\"invalid group member type\")\n\tErrGroupValueNotArray          = errors.New(\"group value must be an array of users\")\n\tErrInvalidHostIP               = errors.New(\"hostname contains invalid IP address\")\n\tErrTagNotDefined               = errors.New(\"tag not found\")\n\tErrAutoApproverNotAlias        = errors.New(\"auto approver is not an alias\")\n\tErrInvalidACLAction            = errors.New(\"invalid ACL action\")\n\tErrInvalidSSHAction            = errors.New(\"invalid SSH action\")\n\tErrInvalidProtocolNumber       = errors.New(\"invalid protocol number\")\n\tErrProtocolLeadingZero         = errors.New(\"leading 0 not permitted in protocol number\")\n\tErrProtocolOutOfRange          = errors.New(\"protocol number out of range (0-255)\")\n\tErrAutogroupNotSupported       = errors.New(\"autogroup not supported in headscale\")\n\tErrAutogroupInternetSrc        = errors.New(\"autogroup:internet can only be used in ACL destinations\")\n\tErrAutogroupSelfSrc            = errors.New(\"\\\"autogroup:self\\\" not valid on the src side of a rule\")\n\tErrAutogroupNotSupportedACLSrc = errors.New(\"autogroup not supported for ACL sources\")","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/policy/v2/types.go#L101-L137","documentation":"Thrown at hscontrol/policy/v2/types.go:912 and 978 when hostport/alias decoding encounters a JSON value whose Go type is not handled by the parser; the error includes the concrete %T. This is a shape error — the JSON structure is wrong for the field, not merely a bad string.","triggerScenarios":"Passing a number or object where a string alias is expected, e.g. {\"dst\": [443]} or {\"src\": {\"user\": \"alice\"}} in a policy fed through the JSON path; also API callers unmarshalling policy JSON with mismatched types. The parser switch hits default and reports the type.","commonSituations":"Programmatic policy generation emitting numbers for ports/aliases; hand-editing HuJSON into invalid JSON shapes; a frontend or API client serializing aliases as objects instead of strings.","solutions":["Make every alias/src/dst entry a JSON string","Inspect the %T in the error to see which field has the wrong type","Generate policies with json.Marshal of the headscale policy types, not hand-built maps"],"exampleFix":"// before\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"group:admins\"], \"dst\": [443]}]}\n// after\n{\"acls\": [{\"action\": \"accept\", \"src\": [\"group:admins\"], \"dst\": [\"*:443\"]}]}","handlingStrategy":"type-guard","validationCode":"// generated policies: assert alias slices are all strings\nfor _, a := range append(rule.Src, rule.Dst...) {\n    if reflect.TypeOf(a).Kind() != reflect.String {\n        return fmt.Errorf(\"alias must be string, got %T\", a)\n    }\n}","typeGuard":"func isStringAlias(v any) bool { _, ok := v.(string); return ok }","tryCatchPattern":"if errors.Is(err, policy.ErrTypeNotSupported) {\n    // %T in message names the offending JSON type; fix the generator\n}","preventionTips":["Generate policy JSON via typed structs, not map[string]any","Quote all aliases and ports as strings","Validate generated JSON shape against the schema"],"tags":["headscale","policy","json","acl","type-error"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}