{"record":{"id":"5123472261ec4e94","repo":"tailscale/tailscale","slug":"logpolicy-config-save-for-v-w","errorCode":null,"errorMessage":"logpolicy.Config.Save for %v: %w","messagePattern":"logpolicy\\.Config\\.Save for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"tsnet/tsnet.go","lineNumber":1101,"sourceCode":"\t\t})\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn authKey, nil\n}\n\nfunc (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker, tsLogf logger.Logf) error {\n\tif testenv.InTest() {\n\t\treturn nil\n\t}\n\tcfgPath := filepath.Join(s.rootPath, \"tailscaled.log.conf\")\n\tlpc, err := logpolicy.ConfigFromFile(cfgPath)\n\tswitch {\n\tcase os.IsNotExist(err):\n\t\tlpc = logpolicy.NewConfig(logtail.CollectionNode)\n\t\tif err := lpc.Save(cfgPath); err != nil {\n\t\t\treturn fmt.Errorf(\"logpolicy.Config.Save for %v: %w\", cfgPath, err)\n\t\t}\n\tcase err != nil:\n\t\treturn fmt.Errorf(\"logpolicy.LoadConfig for %v: %w\", cfgPath, err)\n\t}\n\tif err := lpc.Validate(logtail.CollectionNode); err != nil {\n\t\treturn fmt.Errorf(\"logpolicy.Config.Validate for %v: %w\", cfgPath, err)\n\t}\n\ts.logid = lpc.PublicID\n\n\ts.logbuffer, err = filch.New(filepath.Join(s.rootPath, \"tailscaled\"), filch.Options{ReplaceStderr: false})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error creating filch: %w\", err)\n\t}\n\tclosePool.add(s.logbuffer)\n\tc := logtail.Config{\n\t\tCollection:   lpc.Collection,\n\t\tPrivateID:    lpc.PrivateID,\n\t\tStderr:       io.Discard, // log everything to Buffer","sourceCodeStart":1083,"sourceCodeEnd":1119,"githubUrl":"https://github.com/tailscale/tailscale/blob/57c3357fdb542d26c6f9e9f0b815ae5077e63d77/tsnet/tsnet.go#L1083-L1119","documentation":"On first start, tsnet creates its log configuration file at <Dir>/tailscaled.log.conf; when no such file exists it generates a fresh config and saves it. This error wraps the Save failure, which is almost always a filesystem problem with the Server.Dir: missing directory, no write permission, or disk full.","triggerScenarios":"Server.Dir pointing to a directory that does not exist, is read-only, or is owned by another user; disk full; running the binary as a user without write access to Dir; Dir on a read-only mounted volume.","commonSituations":"Running in a container where the state volume is mounted read-only; forgetting Dir (empty string means current working directory, which may be read-only); non-root service with Dir under /root; ephemeral filesystems that are full.","solutions":["Ensure Server.Dir exists and the process user can write to it: mkdir -p /data/tsnet && chown.","Mount volumes read-write in docker/k8s (remove readOnly: true).","Free disk space if full.","Set an absolute Dir you control, e.g. os.MkdirAll(dir, 0700) before constructing the Server."],"exampleFix":"// before\ns := &tsnet.Server{Dir: \"/var/lib/tsnet\", ...} // /var/lib/tsnet not writable\n\n// after\ndir := \"/var/lib/tsnet\"\nif err := os.MkdirAll(dir, 0o700); err != nil { log.Fatal(err) }\ns := &tsnet.Server{Dir: dir, ...}","handlingStrategy":"validation","validationCode":"// Ensure the Dir is writable before constructing the Server.\nif err := os.MkdirAll(dir, 0o700); err != nil { return err }\nprobe := filepath.Join(dir, \".writable\")\nif err := os.WriteFile(probe, nil, 0o600); err != nil {\n    return fmt.Errorf(\"tsnet Dir %s not writable: %w\", dir, err)\n}\nos.Remove(probe)","typeGuard":"func isLogConfigSaveError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"logpolicy.Config.Save\")\n}","tryCatchPattern":null,"preventionTips":["Always set an absolute Server.Dir you created with os.MkdirAll(dir, 0700).","Mount state volumes read-write in containers; verify with a write probe at startup.","Run the process as the user that owns Dir."],"tags":["go","tailscale","tsnet","logging","filesystem","permissions"],"backgroundTag":"config-file-write-permission","analyzedSha":"57c3357fdb542d26c6f9e9f0b815ae5077e63d77","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}