{"record":{"id":"a678c1bf10d4e183","repo":"can1357/oh-my-pi","slug":"settings-config-is-invalid-and-could-not-be-moved","errorCode":null,"errorMessage":"Settings config is invalid and could not be moved aside: ${filePath}; refusing to overwrite it: ${String(error)}","messagePattern":"Settings config is invalid and could not be moved aside: (.+?); refusing to overwrite it: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/config/settings.ts","lineNumber":1473,"sourceCode":"\t\tif (result.kind === \"invalid\") {\n\t\t\tresult = await this.#quarantineInvalidYamlLocked(writePath, result);\n\t\t\tthis.#quarantinedYamlTargets.set(filePath, writePath);\n\t\t}\n\t\treturn {\n\t\t\tsettings: this.#unwrapYamlLoadResult(filePath, result),\n\t\t\tgeneration,\n\t\t};\n\t}\n\n\tasync #quarantineInvalidYamlLocked(\n\t\tfilePath: string,\n\t\tresult: Extract<YamlLoadResult, { kind: \"invalid\" }>,\n\t): Promise<Extract<YamlLoadResult, { kind: \"invalid\" }>> {\n\t\tconst backupPath = `${filePath}.broken-${Date.now()}-${process.pid}-${randomUUID()}`;\n\t\ttry {\n\t\t\tawait fs.promises.rename(filePath, backupPath);\n\t\t} catch (error) {\n\t\t\tthrow new Error(\n\t\t\t\t`Settings config is invalid and could not be moved aside: ${filePath}; refusing to overwrite it: ${String(error)}`,\n\t\t\t);\n\t\t}\n\t\tlogger.warn(\"Settings: moved invalid config aside\", {\n\t\t\tpath: filePath,\n\t\t\tbackupPath,\n\t\t\terror: String(result.error),\n\t\t});\n\t\treturn { ...result, backupPath };\n\t}\n\n\t#unwrapYamlLoadResult(filePath: string, result: YamlLoadResult): RawSettings | null {\n\t\tswitch (result.kind) {\n\t\t\tcase \"missing\":\n\t\t\t\treturn null;\n\t\t\tcase \"loaded\":\n\t\t\t\treturn result.settings;\n\t\t\tcase \"invalid\":","sourceCodeStart":1455,"sourceCodeEnd":1491,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/config/settings.ts#L1455-L1491","documentation":"Thrown by #quarantineInvalidYamlLocked when the invalid settings YAML cannot be renamed to a .broken-* backup path (fs.rename failed). The loader will not overwrite a file it could not move aside, since that would irrecoverably destroy the user's (broken) settings. The original rename error is embedded via String(error).","triggerScenarios":"Quarantine path executed while the settings file is not renamable: permission denied, read-only filesystem, the file is locked by the OS (Windows AV/indexer), or the directory is no longer writable.","commonSituations":"settings.yaml owned by another user or made read-only; synced/cloud folders holding ephemeral locks; disk full preventing directory updates.","solutions":["Fix file permissions on the settings file and its directory (make writable by the running user)","Check for processes locking the file (editors, antivirus, sync clients) and retry","Manually rename or fix the invalid file, then re-run","Free disk space if the filesystem is full"],"exampleFix":"// before\n$ ls -l ~/.omp/settings.yaml   # -r--r--r--\n// after\n$ chmod u+w ~/.omp/settings.yaml","handlingStrategy":"try-catch","validationCode":"async function settingsWritable(path) {\n  try { await fs.promises.access(path, fs.constants.W_OK); return true; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { await settings.load(); } catch (e) {\n  if (String(e).includes('could not be moved aside')) {\n    logger.error('Fix permissions/locks on settings file, then retry', { error: e });\n  } else throw e;\n}","preventionTips":["Ensure ~/.omp and its files are owned and writable by the running user","Exclude the config directory from antivirus/sync tools that hold locks","Keep sufficient disk space on the volume holding the config"],"tags":["file-system","settings","permissions"],"backgroundTag":"file-permission-denied","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}