{"record":{"id":"cd29a050283beb4c","repo":"tailscale/tailscale","slug":"stat-q-v","errorCode":null,"errorMessage":"stat %q: %v","messagePattern":"stat %q: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"logpolicy/logpolicy.go","lineNumber":388,"sourceCode":"\t\t\t_, err := os.Stat(p)\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tcontinue\n\t\t\t} else if err != nil {\n\t\t\t\treturn false, fmt.Errorf(\"stat %q: %w\", p, err)\n\t\t\t}\n\t\t\treturn true, nil\n\t\t}\n\t\treturn false, nil\n\t}\n\t// move files from d into dir, if they exist.\n\tmoveFiles := func(d string) error {\n\t\tfor _, file := range files {\n\t\t\tsrc := filepath.Join(d, file)\n\t\t\t_, err := os.Stat(src)\n\t\t\tif os.IsNotExist(err) {\n\t\t\t\tcontinue\n\t\t\t} else if err != nil {\n\t\t\t\treturn fmt.Errorf(\"stat %q: %v\", src, err)\n\t\t\t}\n\t\t\tdst := filepath.Join(dir, file)\n\t\t\tbs, err := exec.Command(\"mv\", src, dst).CombinedOutput()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"mv %q %q: %v (%s)\", src, dst, err, bs)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\texistsInRoot, err := checkExists(\"/\")\n\tif err != nil {\n\t\tlogf(\"checking for configs in /: %v\", err)\n\t\treturn\n\t}\n\texistsInCache := false\n\tcacheDir := os.Getenv(\"CACHE_DIRECTORY\")\n\tif cacheDir != \"\" {","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/logpolicy/logpolicy.go#L370-L406","documentation":"Same migration as the other stat error, but inside moveFiles: os.Stat on a source file (in / or /var/cache/tailscale) failed with something other than NotExists. Note this variant uses %v, so it is not errors.Is/As-unwrappable. The error is logged and the move step is abandoned; old files stay where they are.","triggerScenarios":"moveFiles statting a leftover file that the process cannot access (permission denied, EIO), or a dangling path with EACCES on a parent directory; reached only when old files were detected by checkExists.","commonSituations":"Leftover tailscale log files from older packages on systems where permissions changed; read-only or failing mounts under /var/cache.","solutions":["Fix permissions on the reported path so root can stat it, then restart the binary to re-run migration","Manually move/remove the leftover <cmdname>.log* files listed in the path","Safe to ignore: it only delays cleanup of pre-existing log files"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"if _, err := os.Stat(src); err != nil {\n\tif os.IsNotExist(err) {\n\t\tcontinue\n\t}\n\t// non-unwrappable %v message: log and skip this file,\n\t// migration continues with the rest\n\tlog.Printf(\"[non-fatal] skipping %s: %v\", src, err)\n}","preventionTips":["Ensure the migration source dirs (/ and /var/cache/tailscale) remain stat-able by root","Clean up pre-1.x tailscale log files manually if the environment forbids access","Remember this path only runs as root on linux/freebsd/openbsd during upgrade migration"],"tags":["tailscale","logpolicy","filesystem","stat","migration"],"backgroundTag":"filesystem-permission-denied","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}