santifer/career-ops · error
${LOCAL_PATHS_FILE}: refusing "${path}" — ${why}
Error message
${LOCAL_PATHS_FILE}: refusing "${path}" — ${why} What it means
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).
Source
Thrown at update-system.mjs:486
/**
* Read + validate the local declaration file.
*
* Refuses rather than honours anything ambiguous: a path the system layer
* already ships would silently stop updating, and a path that escapes the
* checkout would widen the "never touch" set over files the updater does not
* own. Both throw, naming the offending entry.
*
* @param {string} [root=ROOT] - Repo root to read from.
* @returns {string[]} Extra user-layer paths. Empty when the file is absent.
*/
export function localUserPaths(root = ROOT) {
const file = join(root, LOCAL_PATHS_FILE);
if (!existsSync(file)) return [];
const declared = parseLocalPaths(readFileSync(file, 'utf-8'));
const reject = (path, why) => {
throw new Error(`${LOCAL_PATHS_FILE}: refusing "${path}" — ${why}`);
};
for (const path of declared) {
if (path === LOCAL_PATHS_FILE) {
reject(path, 'the declaration file cannot list itself (it is gitignored, so nothing updates it)');
}
if (path.startsWith('/') || /^[A-Za-z]:[\\/]/.test(path) || path.startsWith('\\')) {
reject(path, 'paths must be repo-relative, not absolute');
}
if (path.split(/[\\/]/).includes('..')) {
reject(path, 'paths must stay inside the repo');
}
const collision = SYSTEM_PATHS.find((sys) =>
sys.endsWith('/') ? path.startsWith(sys) : path === sys,
);
if (collision) {
reject(
path,View on GitHub (pinned to 60398d6549)
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
Example fix
# before (config/local-paths.txt): /home/me/career/notes ../shared-jds # after: data/notes/ jds-local/
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'fs';
const lines = readFileSync('config/local-paths.txt', 'utf-8').split('\n');
const 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'));
if (bad.length) throw new Error('invalid local-paths entries: ' + bad.join(', ')); Prevention
- 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
When it happens
Trigger: 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.
Common situations: Users pasting OS-native absolute paths; trying to protect files that live outside the repository; copying example lines from docs that used absolute paths.
Related errors
- Safety violation (${violation.message}) and revert also fail
- Refusing to write the cover letter outside output/: (empty p
- ${describeGitCommand(args)} timed out after ${timeoutSeconds
- Validation failed (${err.message}) and revert also failed ($
- Update commit failed (files may be staged but not committed)
AI-assisted analysis of santifer/career-ops@60398d6549 (2026-08-20).
Data as JSON: /api/errors/4137ee23e08f9713.
Report an issue: GitHub.