juanfont/headscale · error · types.ErrPolicyUpdateIsDisabled
update is disabled for modes other than 'database'
Error message
update is disabled for modes other than 'database'
What it means
ErrPolicyUpdateIsDisabled (hscontrol/types/policy.go:11) is returned by the policy-update API paths (hscontrol/api/v1/policy.go:115 and hscontrol/api/v2/acl.go:101) when the policy mode is not 'database'. Headscale can source its policy from a file on disk or from the database; only 'database' mode is writable through the API, because file mode must be edited on disk and reloaded.
Source
Thrown at hscontrol/types/policy.go:11
package types
import (
"errors"
"gorm.io/gorm"
)
var (
ErrPolicyNotFound = errors.New("acl policy not found")
ErrPolicyUpdateIsDisabled = errors.New("update is disabled for modes other than 'database'")
)
// Policy represents a policy in the database.
type Policy struct {
gorm.Model
// Data contains the policy in HuJSON format.
Data string
}
View on GitHub (pinned to 565fd254d0)
Solutions
- Set policy.mode: database in config.yaml (and remove policy.path if fully migrating) so API/CLI policy updates work
- Or keep file mode and edit the HuJSON policy file on the server, then reload — no API write is possible
- Check the current mode first via the config or the policy GET endpoint before attempting an update
Example fix
# before (file mode, API updates fail) policy: mode: file path: /etc/headscale/acl.hujson # after (database mode, API updates allowed) policy: mode: database
Defensive patterns
Strategy: validation
Validate before calling
// check policy mode before issuing an API/CLI policy update
if cfg.Policy.Mode != types.PolicyModeDatabase {
return errors.New("policy updates via API require policy.mode: database in config; file mode must be edited on disk")
}
// safe to PUT /api/v1/policy here Try / catch
resp, err := client.SetPolicy(ctx, policy)
if err != nil {
if strings.Contains(err.Error(), types.ErrPolicyUpdateIsDisabled.Error()) {
// switch to database mode or edit policy.path on disk instead
}
} Prevention
- Set policy.mode: database before building tooling that writes policy via API
- Keep file mode only where config is managed by a config-management system
- Surface the mode in admin tooling so operators see why updates fail
When it happens
Trigger: PUT /api/v1/policy or the ACL set endpoint while policy.mode is 'file' (or unset/unrecognized); also CLI commands that call the same API. The handler compares the configured policy mode and responds with an error whose message is exactly 'update is disabled for modes other than database'.
Common situations: Running a config with policy.path set (file mode) but pointing tooling at the API to update ACLs; upgrading from a version where the policy was always file-based; forgetting to add policy.mode: database after switching the policy store to the DB.
Related errors
- invalid action
- test(s) failed
- autogroup:self requires per-node resolution and cannot be re
- references undefined tag
- wildcard (*) is not supported as SSH destination
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/44ccdf4586eb9b6d.
Report an issue: GitHub.