juanfont/headscale · error · ErrInvalidACLAction
action=%q is not supported: %w
Error message
action=%q is not supported: %w
What it means
Action.UnmarshalJSON only accepts the literal "accept" for ACL rules. Any other action string is rejected with ErrInvalidACLAction.
Source
Thrown at hscontrol/policy/v2/types.go:1613
const (
SSHActionAccept SSHAction = "accept"
SSHActionCheck SSHAction = "check"
)
// String returns the string representation of the [Action].
func (a *Action) String() string {
return string(*a)
}
// UnmarshalJSON implements JSON unmarshaling for [Action].
func (a *Action) UnmarshalJSON(b []byte) error {
str := strings.Trim(string(b), `"`)
switch str {
case "accept":
*a = ActionAccept
default:
return fmt.Errorf("action=%q is not supported: %w", str, ErrInvalidACLAction)
}
return nil
}
// MarshalJSON implements JSON marshaling for [Action].
func (a *Action) MarshalJSON() ([]byte, error) {
return json.Marshal(string(*a))
}
// String returns the string representation of the [SSHAction].
func (a *SSHAction) String() string {
return string(*a)
}
// UnmarshalJSON trims surrounding whitespace before matching, lets the
// empty string through (per-rule Validate() surfaces it later), and
// rejects every other unknown value here.View on GitHub (pinned to 565fd254d0)
Solutions
- Remove deny rules entirely — express the complement by only listing what should be allowed
- Use exactly "accept" (lowercase) for permitted rules
Example fix
// before
{"action": "deny", "src": [...], "dst": [...]}
// after
// delete the deny rule; ensure allowed traffic is expressed as:
{"action": "accept", "src": [...], "dst": [...]} Defensive patterns
Strategy: validation
Validate before calling
func validACLAction(s string) bool { return s == "accept" } Prevention
- Remember ACLs are allow-lists: there is no deny action
- Express exclusion by omission, not by deny rules
When it happens
Trigger: An ACL rule with "action": "deny" (or "allow", "drop", or a typo like "Accept") — deny semantics do not exist in this model, rules are allow-lists.
Common situations: Porting firewall-style configs that mix allow/deny rules; capitalization mismatches; outdated policy examples that predate the allow-list-only model.
Related errors
- test(s) failed
- nodeAttrs target does not support this autogroup
- username must contain @
- group must start with 'group:'
- tag must start with 'tag:'
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/5fd3bfb2a8586ef1.
Report an issue: GitHub.