{"record":{"id":"fb47610a5bd27b22","repo":"AutoDarkMode/Windows-Auto-Night-Mode","slug":"saving-to-path-failed-after-10-retries","errorCode":null,"errorMessage":"Saving to {path} failed after 10 retries","messagePattern":"Saving to (.+?) failed after 10 retries","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"AutoDarkModeLib/AdmConfigBuilder.cs","lineNumber":158,"sourceCode":"\n        ISerializer yamlSerializer = yamlBuilder.Build();\n\n        string yamlConfig = yamlSerializer.Serialize(obj);\n        for (int i = 0; i < 10; i++)\n        {\n            if (IsFileLocked(new FileInfo(path)))\n            {\n                Thread.Sleep(500);\n            }\n            else\n            {\n                using StreamWriter writer = new(File.Open(path, FileMode.Create, FileAccess.Write));\n                writer.WriteLine(yamlConfig);\n                writer.Close();\n                return;\n            }\n        }\n        throw new TimeoutException($\"Saving to {path} failed after 10 retries\");\n    }\n\n    private string LoadFile(string path)\n    {\n        Loading = true;\n        Exception readException = new TimeoutException($\"Reading from {path} failed after 3 retries\");\n        for (int i = 0; i < 3; i++)\n        {\n            try\n            {\n                using StreamReader dataReader = new(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));\n                return dataReader.ReadToEnd();\n            }\n            catch (Exception ex)\n            {\n                readException = ex;\n                Thread.Sleep(500);\n            }","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/AutoDarkMode/Windows-Auto-Night-Mode/blob/c15b28e92138a1baff6165bbca80842f06cb819f/AutoDarkModeLib/AdmConfigBuilder.cs#L140-L176","documentation":"Thrown by AdmConfigBuilder.SaveConfig after 10 failed retry attempts to write a YAML config file because the file remained locked (IsFileLocked returns true each iteration, sleeping 500ms between tries — up to ~5s total). The TimeoutException indicates a persistent file lock held by another process/handle that the 10-retry loop could not out-wait.","triggerScenarios":"SaveConfig(path, obj) loops 10 times; each iteration calls IsFileLocked(new FileInfo(path)) which tries to open the file with FileShare.None — if it throws IOException the file is considered locked. After 10 locked iterations, TimeoutException is thrown at line 158.","commonSituations":"Antivirus or backup software scanning the config file; another ADM process (svc + app) writing simultaneously; cloud-sync (OneDrive) holding a write lock; file on a network share with opportunistic locking; editor with the YAML file open; the writer itself previously crashed mid-write leaving a handle.","solutions":["Close any editor/preview that has the config YAML open, then retry the save.","Exclude the %AppData%\\AutoDarkMode folder from real-time AV/cloud-sync scanning.","Ensure only one instance of the app+service is writing; serialize writes via the config-updated event.","Increase retries / use a randomized backoff, or write to a temp file then atomic-rename so readers never see a partial file.","Check for stale file handles from a crashed process (reboot if necessary)."],"exampleFix":"// before — 10x spin on IsFileLocked, writes non-atomically\nfor (int i = 0; i < 10; i++)\n{\n    if (IsFileLocked(new FileInfo(path))) { Thread.Sleep(500); continue; }\n    using StreamWriter writer = new(File.Open(path, FileMode.Create, FileAccess.Write));\n    writer.WriteLine(yamlConfig);\n    return;\n}\nthrow new TimeoutException(...);\n\n// after — write temp + atomic replace, retry on IOException with backoff\nstring tmp = path + \".tmp\";\nfor (int i = 0; i < 10; i++)\n{\n    try\n    {\n        File.WriteAllText(tmp, yamlConfig);\n        if (File.Exists(path)) File.Replace(tmp, path, null);\n        else File.Move(tmp, path);\n        return;\n    }\n    catch (IOException) { Thread.Sleep(500); }\n}\nthrow new TimeoutException($\"Saving to {path} failed after 10 retries\");","handlingStrategy":"retry","validationCode":"// Check whether the config file is locked before attempting to save\npublic static bool IsConfigWritable(string path)\n{\n    if (!File.Exists(path)) return true;\n    try { using var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.None); return true; }\n    catch (IOException) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    builder.Save();\n}\ncatch (TimeoutException ex)\n{\n    // file stayed locked through 10 retries — surface to user, offer config backup\n    logger.LogError(ex, \"Config save timed out\");\n    throw;\n}","preventionTips":["Write config via temp-file + atomic File.Replace so readers never block and partial files are impossible.","Exclude %AppData%\\AutoDarkMode from real-time AV and cloud-sync scanning.","Serialize writes from app and service through the ConfigUpdated event to avoid concurrent Save() calls.","Use jittered backoff instead of fixed 500ms sleeps to avoid lock-step contention.","Keep the config on a local drive, not a network share with op-locking."],"tags":["file-io","file-lock","yaml","configuration","retry","timeout"],"backgroundTag":null,"analyzedSha":"c15b28e92138a1baff6165bbca80842f06cb819f","analyzedAt":"2026-08-13T21:04:56.049Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}