juanfont/headscale · error · ErrUnsupportedPolicyMode

unsupported policy mode

Error message

unsupported policy mode

What it means

ErrUnsupportedPolicyMode is a sentinel error in hscontrol/state declaring that the configured policy mode is neither "file" nor "db". It is produced by wrapping with the offending mode string (hscontrol/state/debug.go:213: fmt.Errorf("%w: %s", ErrUnsupportedPolicyMode, s.cfg.Policy.Mode)). It surfaces during server startup / debug dumps when the policy.mode config value cannot be mapped to a policy manager backend.

Source

Thrown at hscontrol/state/state.go:66

	// defaultRegisterCacheMaxEntries is the default upper bound on the number
	// of pending registration entries the auth cache will hold. With a 15-minute
	// TTL and a stripped-down RegistrationData payload (~200 bytes per entry),
	// 1024 entries cap the worst-case cache footprint at well under 1 MiB even
	// under sustained unauthenticated cache-fill attempts.
	defaultRegisterCacheMaxEntries = 1024

	// defaultNodeStoreBatchSize is the default number of write operations to batch
	// before rebuilding the in-memory node snapshot.
	defaultNodeStoreBatchSize = 100

	// defaultNodeStoreBatchTimeout is the default maximum time to wait before
	// processing a partial batch of node operations.
	defaultNodeStoreBatchTimeout = 500 * time.Millisecond
)

// ErrUnsupportedPolicyMode is returned for invalid policy modes. Valid modes are "file" and "db".
var ErrUnsupportedPolicyMode = errors.New("unsupported policy mode")

// ErrNodeNotFound is returned when a node cannot be found by its ID.
var ErrNodeNotFound = errors.New("node not found")

// ErrInvalidNodeView is returned when an invalid node view is provided.
var ErrInvalidNodeView = errors.New("invalid node view provided")

// ErrNodeNotInNodeStore is returned when a node no longer exists in the [NodeStore].
var ErrNodeNotInNodeStore = errors.New("node no longer exists in NodeStore")

// ErrNodeNameNotUnique is returned when a node name is not unique.
var ErrNodeNameNotUnique = errors.New("node name is not unique")

// nodeUpdateColumns lists all Node columns that should be written
// during a struct-based GORM Updates() call.  Listing them explicitly
// forces GORM to include nil/zero-value fields (e.g. UserID=nil when
// converting a user-owned node to tagged) that struct-based Updates()
// would otherwise silently skip.

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Set policy.mode to "file" if the ACL policy lives in a HuJSON/JSON file referenced by policy.path
  2. Set policy.mode to "db" if policies are managed via the API/database
  3. Verify there is no YAML typo or stray whitespace in the policy.mode value
  4. After fixing, restart headscale and confirm startup logs no longer report the error

Example fix

# before
policy:
  mode: database

# after
policy:
  mode: db
Defensive patterns

Strategy: validation

Validate before calling

validModes := map[string]bool{"file": true, "db": true}
if !validModes[cfg.Policy.Mode] {
    return fmt.Errorf("invalid policy mode %q: use file or db", cfg.Policy.Mode)
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Setting policy.mode in the headscale config to anything other than "file" or "db" (e.g. "database", "acl", typo like "filE"), then triggering the code path in hscontrol/state/debug.go:213 that formats the active policy for a debug dump.

Common situations: Upgrading from older headscale versions where policy config keys changed; copy-pasting configs from tutorials that use invalid mode names; leaving a placeholder value in policy.mode.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/b75332c98a23df4c. Report an issue: GitHub.