abhigyanpatwari/GitNexus · error · Error
Refusing symlink in auto-sync clone root path: ${current}
Error message
Refusing symlink in auto-sync clone root path: ${current} What it means
Thrown by assertNoSymlinkPath (via resolveConfiguredCloneRoot) after lstat reveals that some component of the clone-root path is a symbolic link. The guard walks the path from the filesystem root down, lstat-ing each segment; a symlink anywhere in the chain (even in a parent directory) is rejected because the real containment checks and dangerous-root checks were performed on the lexical path and a symlink could redirect writes to an unvetted location.
Source
Thrown at gitnexus/src/core/auto-sync/path-security.ts:254
}
}
}
async function assertNoSymlinkPath(root: string): Promise<void> {
const parsed = path.parse(root);
let current = parsed.root;
const parts = root.slice(parsed.root.length).split(path.sep).filter(Boolean);
for (const part of parts) {
current = path.join(current, part);
let stat;
try {
stat = await fs.lstat(current);
} catch (err: unknown) {
if ((err as NodeJS.ErrnoException).code === 'ENOENT') break;
throw err;
}
if (stat.isSymbolicLink())
throw new Error(`Refusing symlink in auto-sync clone root path: ${current}`);
}
}
export async function assertDirectoryOwnerAndPermissions(root: string): Promise<void> {
const stat = await fs.stat(root);
if (!stat.isDirectory()) throw new Error(`auto-sync clone root is not a directory: ${root}`);
// POSIX uid/mode have no meaning on Windows, and this runs on every tick for
// every project, so throwing here failed 100% of repos forever while `watch
// status` still read `running`. Skip the ownership assertions rather than the
// whole feature: the caller's other guards — dangerous-root rejection
// (including the Windows system roots), symlink refusal, realpath containment
// and the GitNexus-internal-root check — all still apply, and managed git runs
// with `core.hooksPath` pinned to the null device.
if (process.platform === 'win32') return;
if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) {
throw new Error(`auto-sync clone root is owned by uid ${stat.uid}, not current process uid`);
}
const mode = stat.mode & 0o777;View on GitHub (pinned to 0d1aed942f)
Solutions
- Replace the symlink with a real directory (mv the target into place or bind-mount it)
- Point the auto-sync configuration directly at the symlink's resolved real path
- Use a physical directory on the same filesystem to avoid needing the symlink at all
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at gitnexus/src/core/auto-sync/path-security.ts:254 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of abhigyanpatwari/GitNexus@0d1aed942f (2026-09-08).
Data as JSON: /api/errors/d16ef28be568ca92.
Report an issue: GitHub.