{"record":{"id":"44ccdf4586eb9b6d","repo":"juanfont/headscale","slug":"update-is-disabled-for-modes-other-than-database","errorCode":null,"errorMessage":"update is disabled for modes other than 'database'","messagePattern":"update is disabled for modes other than 'database'","errorType":"http","errorClass":"types.ErrPolicyUpdateIsDisabled","httpStatus":400,"severity":"error","filePath":"hscontrol/types/policy.go","lineNumber":11,"sourceCode":"package types\n\nimport (\n\t\"errors\"\n\n\t\"gorm.io/gorm\"\n)\n\nvar (\n\tErrPolicyNotFound         = errors.New(\"acl policy not found\")\n\tErrPolicyUpdateIsDisabled = errors.New(\"update is disabled for modes other than 'database'\")\n)\n\n// Policy represents a policy in the database.\ntype Policy struct {\n\tgorm.Model\n\n\t// Data contains the policy in HuJSON format.\n\tData string\n}\n","sourceCodeStart":1,"sourceCodeEnd":21,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/hscontrol/types/policy.go#L1-L21","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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"],"exampleFix":"# before (file mode, API updates fail)\npolicy:\n  mode: file\n  path: /etc/headscale/acl.hujson\n\n# after (database mode, API updates allowed)\npolicy:\n  mode: database","handlingStrategy":"validation","validationCode":"// check policy mode before issuing an API/CLI policy update\nif cfg.Policy.Mode != types.PolicyModeDatabase {\n\treturn errors.New(\"policy updates via API require policy.mode: database in config; file mode must be edited on disk\")\n}\n// safe to PUT /api/v1/policy here","typeGuard":null,"tryCatchPattern":"resp, err := client.SetPolicy(ctx, policy)\nif err != nil {\n\tif strings.Contains(err.Error(), types.ErrPolicyUpdateIsDisabled.Error()) {\n\t\t// switch to database mode or edit policy.path on disk instead\n\t}\n}","preventionTips":["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"],"tags":["policy","acl","api","config"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}