juanfont/headscale · error
test entry must have at least one of "accept" or "deny"
Error message
test entry must have at least one of "accept" or "deny"
What it means
ErrTestEmptyAssertions is returned by validateTests (hscontrol/policy/v2/types.go:3202) when a policy "tests" entry declares neither an "accept" nor a "deny" list (both empty). A test entry must assert an expected outcome for at least one destination, otherwise it verifies nothing. It is collected into a multi-error wrapped by errPolicyTestsFailed with the test's index.
Source
Thrown at hscontrol/policy/v2/types.go:147
ErrInvalidACLAction = errors.New("invalid ACL action")
ErrInvalidSSHAction = errors.New("invalid SSH action")
ErrInvalidProtocolNumber = errors.New("invalid protocol number")
ErrProtocolLeadingZero = errors.New("leading 0 not permitted in protocol number")
ErrProtocolOutOfRange = errors.New("protocol number out of range (0-255)")
ErrAutogroupNotSupported = errors.New("autogroup not supported in headscale")
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{}, errView on GitHub (pinned to 565fd254d0)
Solutions
- Add at least one destination to "accept" or "deny" in the flagged test entry (index is in the message)
- If the entry was scaffolding, delete it entirely
- Re-run policy validation to confirm no further test-shape errors follow (they are reported together)
Example fix
// before
"tests": [{"src": "user1", "proto": "tcp"}]
// after
"tests": [{"src": "user1", "proto": "tcp", "accept": ["tag:server:22"]}] Defensive patterns
Strategy: validation
Validate before calling
// Validate a tests entry shape before adding it to the policy object
func testEntryHasAssertion(t PolicyTest) bool {
return len(t.Accept) > 0 || len(t.Deny) > 0
} Try / catch
if err := policyv2.LoadPolicy(buf); err != nil {
if errors.Is(err, policyv2.ErrTestEmptyAssertions) {
// remove or complete the flagged tests entry (index in message)
}
} Prevention
- Treat tests entries as incomplete until they have src plus accept or deny
- Never merge scaffolding entries with empty assertion lists
- Run policy validation on every policy edit, not only on deploy
When it happens
Trigger: A tests block entry containing only src/proto, e.g. {"src": "user1", "proto": "tcp", "accept": []} or an entry with both accept and deny keys omitted entirely. Raised during policy validation when len(t.Accept) == 0 && len(t.Deny) == 0.
Common situations: Writing a first tests entry and forgetting the assertion half; YAML-to-HuJSON conversion dropping empty lists; commenting out assertions while debugging and forgetting to restore them; templates that scaffold {"src": ..., "accept": []}.
Related errors
- test protocol must be tcp, udp, sctp, or empty
- test destination port must be a single port
- test destination must be a single host, not a CIDR range
- SSH tests entry must have a non-empty src
- SSH tests entry must have at least one dst
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/2624cafa0fae4b8d.
Report an issue: GitHub.