{"record":{"id":"4137ee23e08f9713","repo":"santifer/career-ops","slug":"local-paths-file-refusing-path-why","errorCode":null,"errorMessage":"${LOCAL_PATHS_FILE}: refusing \"${path}\" — ${why}","messagePattern":"(.+?): refusing \"(.+?)\" — (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"update-system.mjs","lineNumber":486,"sourceCode":"\n/**\n * Read + validate the local declaration file.\n *\n * Refuses rather than honours anything ambiguous: a path the system layer\n * already ships would silently stop updating, and a path that escapes the\n * checkout would widen the \"never touch\" set over files the updater does not\n * own. Both throw, naming the offending entry.\n *\n * @param {string} [root=ROOT] - Repo root to read from.\n * @returns {string[]} Extra user-layer paths. Empty when the file is absent.\n */\nexport function localUserPaths(root = ROOT) {\n  const file = join(root, LOCAL_PATHS_FILE);\n  if (!existsSync(file)) return [];\n\n  const declared = parseLocalPaths(readFileSync(file, 'utf-8'));\n  const reject = (path, why) => {\n    throw new Error(`${LOCAL_PATHS_FILE}: refusing \"${path}\" — ${why}`);\n  };\n\n  for (const path of declared) {\n    if (path === LOCAL_PATHS_FILE) {\n      reject(path, 'the declaration file cannot list itself (it is gitignored, so nothing updates it)');\n    }\n    if (path.startsWith('/') || /^[A-Za-z]:[\\\\/]/.test(path) || path.startsWith('\\\\')) {\n      reject(path, 'paths must be repo-relative, not absolute');\n    }\n    if (path.split(/[\\\\/]/).includes('..')) {\n      reject(path, 'paths must stay inside the repo');\n    }\n    const collision = SYSTEM_PATHS.find((sys) =>\n      sys.endsWith('/') ? path.startsWith(sys) : path === sys,\n    );\n    if (collision) {\n      reject(\n        path,","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/santifer/career-ops/blob/60398d6549a46f5266929538af21cfab94badc75/update-system.mjs#L468-L504","documentation":"config/local-paths.txt declares extra user-layer paths the auto-updater must never touch. localUserPaths() validates every declared entry and hard-refuses three shapes: absolute paths (POSIX leading / or Windows drive/backslash forms), paths containing a .. segment, and the file listing itself (it is gitignored, so an updater checkout could never update it — self-listing is meaningless and widens the never-touch set incorrectly).","triggerScenarios":"Pasting /home/me/notes or C:\\cv\\extra from a file dialog into config/local-paths.txt; adding ../shared-jds to protect files outside the repo; listing config/local-paths.txt as its own protected path.","commonSituations":"Users pasting OS-native absolute paths; trying to protect files that live outside the repository; copying example lines from docs that used absolute paths.","solutions":["Use repo-relative paths: data/notes/ instead of /home/me/data/notes","Remove .. segments — the file can only declare paths inside the repo","Delete the self-referencing config/local-paths.txt line if present","Re-run `node update-system.mjs check` (or apply) — the file is parsed on every update, so the fix takes effect immediately"],"exampleFix":"# before (config/local-paths.txt):\n/home/me/career/notes\n../shared-jds\n\n# after:\ndata/notes/\njds-local/","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'fs';\nconst lines = readFileSync('config/local-paths.txt', 'utf-8').split('\\n');\nconst bad = lines.map((s) => s.trim()).filter((p) => p && !p.startsWith('#') && (p.startsWith('/') || p.startsWith('\\\\') || /^[A-Za-z]:[\\\\/]/.test(p) || p.split(/[\\\\/]/).includes('..') || p === 'config/local-paths.txt'));\nif (bad.length) throw new Error('invalid local-paths entries: ' + bad.join(', '));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write only repo-relative paths, one per line","Convert pasted OS paths by stripping the repo prefix before saving","Run node update-system.mjs check after editing the file — it validates entries before any update runs"],"tags":["configuration","path-validation","updater","user-layer"],"backgroundTag":"path-validation-failed","analyzedSha":"60398d6549a46f5266929538af21cfab94badc75","analyzedAt":"2026-08-20T23:00:06.764Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}