juanfont/headscale · error
is above the max (168h)
Error message
is above the max (168h)
What it means
SSH validation error (hscontrol/policy/v2/types.go:2863, formatted as 'checkPeriod <dur> is above the max (168h)'): an SSH rule with action "check" specifies a checkPeriod longer than 7 days (168h). Tailscale caps re-check intervals so periodic SSH re-authentication cannot be effectively disabled by a huge period.
Source
Thrown at hscontrol/policy/v2/types.go:47
json.DefaultOptionsV2(),
json.MatchCaseInsensitiveNames(true),
json.RejectUnknownMembers(true),
}
const Wildcard = Asterix(0)
var ErrAutogroupSelfRequiresPerNodeResolution = errors.New("autogroup:self requires per-node resolution and cannot be resolved in this context")
var ErrUndefinedTagReference = errors.New("references undefined tag")
// SSH validation errors.
var (
ErrSSHTagSourceToUserDest = errors.New("tags in SSH source cannot access user-owned devices")
ErrSSHUserDestRequiresSameUser = errors.New("user destination requires source to contain only that same user")
ErrSSHAutogroupSelfRequiresUserSource = errors.New("autogroup:self destination requires source to contain only users or groups, not tags or autogroup:tagged")
ErrSSHTagSourceToAutogroupMember = errors.New("tags in SSH source cannot access autogroup:member (user-owned devices)")
ErrSSHWildcardDestination = errors.New("wildcard (*) is not supported as SSH destination")
ErrSSHCheckPeriodAboveMax = errors.New("is above the max (168h)")
ErrSSHCheckPeriodNegative = errors.New("must be a positive duration")
ErrSSHCheckPeriodOnNonCheck = errors.New("checkPeriod is only valid with action \"check\"")
ErrInvalidLocalpart = errors.New("invalid localpart format, must be localpart:*@<domain>")
ErrSSHUsersMustBeSpecified = errors.New("users must be specified")
ErrSSHUserInvalid = errors.New("is not valid")
ErrSSHAcceptEnvEmpty = errors.New("acceptEnv values cannot be empty")
ErrSSHActionMustBeSpecified = errors.New("action must be specified")
ErrSSHActionInvalid = errors.New("is not a valid action")
ErrSSHDestinationHostAlias = errors.New("invalid dst")
ErrTagNameMustStartWithLetter = errors.New("tag names must start with a letter, after 'tag:'")
ErrGroupMembersCannotBeRecursive = errors.New("group members cannot be recursive")
)
// SSH check period constants per Tailscale docs:
// https://tailscale.com/docs/features/tailscale-ssh#checkperiod
// SaaS imposes no minimum (0s is accepted) so headscale matches.
const (
SSHCheckPeriodDefault = 12 * time.HourView on GitHub (pinned to 565fd254d0)
Solutions
- Lower checkPeriod to at most "168h"
- If the intent was 'never re-check', use action "accept" instead — but prefer short check periods for least privilege
- Automate the re-auth prompt acceptance rather than stretching the period
Example fix
// before
{"src": ["group:ops"], "dst": ["tag:server"], "users": ["root"], "action": "check", "checkPeriod": "720h"}
// after
{"src": ["group:ops"], "dst": ["tag:server"], "users": ["root"], "action": "check", "checkPeriod": "168h"} Defensive patterns
Strategy: validation
Validate before calling
func checkPeriodValid(d time.Duration) bool { return d > 0 && d <= 168*time.Hour } Prevention
- Cap checkPeriod at 168h in policy generation tooling
- Prefer shorter periods; use 'accept' only deliberately
- Unit-test duration parsing boundary values (see types_test.go:4490)
When it happens
Trigger: {"action": "check", "checkPeriod": "720h"} or any duration > 168h; types_test.go:4490-4615 exercises exactly these boundary cases. Also triggered by unit-less values parsed as hours depending on the duration format accepted.
Common situations: Trying to make check 'once a quarter' to avoid prompts; typo adding a zero (720h vs 72h); migrating configs from tooling that did not enforce the cap.
Related errors
- wildcard (*) is not supported as SSH destination
- 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
- tags in SSH source cannot access autogroup:member (user-owne
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/a0c1f4c417c62331.
Report an issue: GitHub.