juanfont/headscale · error
nodeAttrs uses a feature headscale does not yet support
Error message
nodeAttrs uses a feature headscale does not yet support
What it means
Returned at policy validation (hscontrol/policy/v2/types.go:2714) when a nodeAttrs entry uses a capability listed in nodeAttrUnsupportedCaps — currently tailcfg.NodeAttrFunnel. headscale can parse these caps but lacks the server-side machinery to make them work, so delivering them would let nodes advertise a broken feature; the error points at the tracking GitHub issue.
Source
Thrown at hscontrol/policy/v2/types.go:91
// Grant validation errors.
var (
ErrGrantMissingIPOrApp = errors.New("ip and app can not both be empty")
ErrGrantViaNotATag = errors.New("via can only be a tag")
ErrProtocolPortInvalidFormat = errors.New("expected only one colon in Internet protocol and port type")
ErrCapNameInvalidForm = errors.New("capability name must have the form {domain}/{path}")
ErrCapNameTailscaleDomain = errors.New("capability name must not be in the tailscale.com domain")
ErrGrantAutogroupSelfInvalidSource = errors.New("autogroup:self can only be used with users, groups, or supported autogroups")
ErrGrantAppWithAutogroupInternet = errors.New("cannot use app grants with autogroup:internet")
ErrGrantDefaultRouteCIDR = errors.New("to allow all IP addresses, use \"*\" or \"autogroup:internet\"")
)
// NodeAttrs validation errors.
var (
ErrNodeAttrsIPPoolReserved = errors.New("nodeAttrs ipPool must not overlap reserved Tailscale ranges")
ErrNodeAttrsIPPoolOutOfRange = errors.New("nodeAttrs ipPool must be within 100.64.0.0/10")
ErrNodeAttrsAutogroupNotAllowed = errors.New("nodeAttrs target does not support this autogroup")
ErrNodeAttrUnsupported = errors.New("nodeAttrs uses a feature headscale does not yet support")
ErrNodeAttrIPPoolUnsupported = errors.New("nodeAttrs ipPool requires the IP allocator (https://github.com/juanfont/headscale/issues/2912)")
ErrNodeAttrTargetUnsupported = errors.New("nodeAttrs target alias type is not supported")
)
// nodeAttrUnsupportedCaps lists caps that headscale parses but cannot act on
// today. Each entry maps to the tracking issue an operator can follow. The
// caps are accepted by Tailscale SaaS, but delivering them via headscale
// without the matching server-side machinery would be misleading — nodes
// would advertise a feature that does not work. Reject at policy load and
// point operators at the issue.
var nodeAttrUnsupportedCaps = map[tailcfg.NodeCapability]string{
tailcfg.NodeAttrFunnel: "https://github.com/juanfont/headscale/issues/2527",
}
// Policy validation errors.
var (
ErrInvalidUsername = errors.New("username must contain @")
ErrUserNotFound = errors.New("user not found")View on GitHub (pinned to 565fd254d0)
Solutions
- Remove the funnel attr from nodeAttrs and enable Funnel via headscale's funnel config/flags instead of policy attrs
- Follow the tracking issue in the error message and wait for server-side support
- Use plain grants/ACLs for the underlying access; do not rely on Funnel attr until the issue closes
Example fix
// before
{"nodeAttrs": [{"target": ["autogroup:member"], "attrs": ["funnel"]}]}
// after
{"nodeAttrs": [{"target": ["autogroup:member"], "attrs": []}]} Defensive patterns
Strategy: validation
Validate before calling
unsupported := map[string]bool{"funnel": true}
for _, na := range policy.NodeAttrs {
for _, a := range na.Attrs {
if unsupported[a] {
return fmt.Errorf("attr %q unsupported by headscale", a)
}
}
} Type guard
func isSupportedNodeAttr(attr string) bool { return attr != "funnel" } Try / catch
if errors.Is(err, policy.ErrNodeAttrUnsupported) {
// strip the listed attr; the error names the tracking issue
} Prevention
- Do not port Tailscale SaaS funnel nodeAttrs into headscale
- Enable Funnel via headscale configuration, not policy attrs
- Re-check nodeAttrUnsupportedCaps when upgrading headscale
When it happens
Trigger: A policy containing nodeAttrs whose attrs array includes "funnel" (tailcfg.NodeAttrFunnel). The error message embeds the capability name and its tracking issue URL (currently https://github.com/juanfont/headscale/issues/2527).
Common situations: Porting a Tailscale SaaS ACL that enables Funnel via nodeAttrs; enabling funnel in the policy expecting the config flag alone was insufficient; following upstream Tailscale docs that assume the SaaS control plane.
Related errors
- nodeAttrs ipPool requires the IP allocator (https://github.c
- nodeAttrs target does not support this autogroup
- invalid action
- tag owner is not an Alias
- test(s) failed
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/fd4967e266aed1af.
Report an issue: GitHub.