juanfont/headscale · error · ErrSSHTestEmptyDst
SSH tests entry must have at least one dst
Error message
SSH tests entry must have at least one dst
What it means
ErrSSHTestEmptyDst is returned by validateSSHTests (hscontrol/policy/v2/types.go:3287) when an sshTests entry declares an empty or missing "dst" list. Each entry must name at least one SSH-reachable host for the assertion to mean anything; the destination list cannot be left blank or as [].
Source
Thrown at hscontrol/policy/v2/types.go:153
ErrAutogroupInternetSrc = errors.New("autogroup:internet can only be used in ACL destinations")
ErrAutogroupSelfSrc = errors.New("\"autogroup:self\" not valid on the src side of a rule")
ErrAutogroupNotSupportedACLSrc = errors.New("autogroup not supported for ACL sources")
ErrAutogroupNotSupportedACLDst = errors.New("autogroup not supported for ACL destinations")
ErrAutogroupDangerAllDst = errors.New("cannot use autogroup:danger-all as a dst")
ErrAutogroupNotSupportedSSHSrc = errors.New("autogroup not supported for SSH sources")
ErrAutogroupNotSupportedSSHDst = errors.New("autogroup not supported for SSH destinations")
ErrHostNotDefined = errors.New("host not defined in policy")
ErrSSHSourceAliasNotSupported = errors.New("alias not supported for SSH source")
ErrSSHDestAliasNotSupported = errors.New("alias not supported for SSH destination")
ErrUnknownField = errors.New("unknown field")
ErrProtocolNoSpecificPorts = errors.New("protocol does not support specific ports")
ErrTestEmptyAssertions = errors.New("test entry must have at least one of \"accept\" or \"deny\"")
ErrTestProtocolNotAllowed = errors.New("test protocol must be tcp, udp, sctp, or empty")
ErrTestDestinationMultiPort = errors.New("test destination port must be a single port")
ErrTestDestinationCIDR = errors.New("test destination must be a single host, not a CIDR range")
ErrAutogroupInternetTestDst = errors.New("autogroup:internet not valid as a test destination")
ErrSSHTestEmptySrc = errors.New("SSH tests entry must have a non-empty src")
ErrSSHTestEmptyDst = errors.New("SSH tests entry must have at least one dst")
ErrSSHTestDstUnknownTag = errors.New("SSH tests dst contains unknown tag")
ErrSSHTestDstDisallowedElement = errors.New("SSH tests dst contains disallowed element")
)
type resolved struct {
ips netipx.IPSet
}
func newResolved(ipb *netipx.IPSetBuilder) (resolved, error) {
ips, err := ipb.IPSet()
if err != nil {
return resolved{}, err
}
return resolved{ips: *ips}, nil
}
func newResolvedAddresses(ips *netipx.IPSet, err error) (ResolvedAddresses, error) {View on GitHub (pinned to 565fd254d0)
Solutions
- Add at least one dst to the flagged entry — a tag declared in tagOwners, a bare IP, or an allowed autogroup
- Delete the entry if it was scaffolding
- Re-validate the policy to catch follow-on dst shape errors
Example fix
// before
"sshTests": [{"src": "group:admin", "accept": ["root"]}]
// after
"sshTests": [{"src": "group:admin", "dst": ["tag:server"], "accept": ["root"]}] Defensive patterns
Strategy: validation
Validate before calling
func sshTestHasDst(t SSHPolicyTest) bool { return len(t.Dst) > 0 } Try / catch
if errors.Is(err, policyv2.ErrSSHTestEmptyDst) {
// add at least one tag/IP/autogroup dst to the flagged entry
} Prevention
- Fill dst before accept/deny when authoring ssh tests
- Reject empty arrays in policy templates
- Validate the full sshTests shape on every policy edit
When it happens
Trigger: An sshTests entry like {"src": "user:alice", "accept": ["root"]} with dst omitted, or "dst": []. Raised when len(t.Dst) == 0 during policy validation.
Common situations: Filling in the src and users first and forgetting the target; a templating step that produces empty dst arrays; refactoring a policy and dropping the dst line accidentally.
Related errors
- SSH tests entry must have a non-empty src
- SSH tests dst contains unknown tag
- tags in SSH source cannot access user-owned devices
- user destination requires source to contain only that same u
- autogroup:self destination requires source to contain only u
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/bd91d3e20bb9a8bd.
Report an issue: GitHub.