abhigyanpatwari/GitNexus · error
${filename} must not be a symbolic link
Error message
${filename} must not be a symbolic link What it means
Thrown by readRepoControlFile in repo-control-file.ts after an lstat shows the requested control file is a symbolic link. Repo control files must be plain, first-party files inside the repo; symlinks could redirect reads to attacker-controlled or out-of-repo targets, so they are rejected outright as a TOCTOU-hardening measure.
Source
Thrown at gitnexus/src/config/repo-control-file.ts:21
export const MAX_REPO_CONTROL_FILE_BYTES = 1024 * 1024;
/** Read a bounded, regular control file owned by the repository root. */
export async function readRepoControlFile(
repoRoot: string,
filename: string,
): Promise<string | null> {
const requestedRoot = path.resolve(repoRoot);
const requested = path.resolve(requestedRoot, filename);
const relative = path.relative(requestedRoot, requested);
if (relative.startsWith('..') || path.isAbsolute(relative)) {
throw new Error(`${filename} resolves outside the repository root`);
}
try {
const canonicalRoot = fs.realpathSync(requestedRoot);
const beforeOpen = fs.lstatSync(requested);
if (beforeOpen.isSymbolicLink()) throw new Error(`${filename} must not be a symbolic link`);
if (!beforeOpen.isFile()) throw new Error(`${filename} must be a regular file`);
if (beforeOpen.nlink !== 1) throw new Error(`${filename} must not be a hard link`);
if (beforeOpen.size > MAX_REPO_CONTROL_FILE_BYTES) {
throw new Error(`${filename} exceeds ${MAX_REPO_CONTROL_FILE_BYTES} bytes`);
}
return await new Promise<string>((resolve, reject) => {
const stream = fs.createReadStream(requested, {
flags: 'r',
start: 0,
end: MAX_REPO_CONTROL_FILE_BYTES,
autoClose: true,
});
const chunks: Buffer[] = [];
let totalBytes = 0;
let validated = false;
let settled = false;
const finish = (value: string): void => {View on GitHub (pinned to 52924ef12c)
Solutions
- Replace the symlink with a real file: `rm .gitnexusrc && cp /path/to/target .gitnexusrc`.
- If a dotfile manager created the link, configure it to copy instead of symlink for this path.
- Keep per-repo copies of the control file instead of sharing one via symlinks.
- Update test fixtures to copy files into the repo rather than linking them.
Example fix
// before ln -s ~/dotfiles/.gitnexusrc .gitnexusrc // after rm .gitnexusrc cp ~/dotfiles/.gitnexusrc .gitnexusrc
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
import path from 'node:path';
export function assertRegularFile(repoRoot: string, filename: string): void {
const st = fs.lstatSync(path.resolve(repoRoot, filename));
if (st.isSymbolicLink()) throw new Error(`${filename} must not be a symbolic link; replace with a real file`);
if (!st.isFile()) throw new Error(`${filename} must be a regular file`);
} Try / catch
try {
const cfg = await readRepoControlFile(repoRoot, filename);
} catch (e) {
if (e.message.includes('must not be a symbolic link')) {
console.error(`Replace symlink ${filename} with a real file (rm + cp).`);
process.exitCode = 1;
} else throw e;
} Prevention
- Configure dotfile managers (stow, chezmoi) to copy rather than symlink .gitnexusrc.
- Check `ls -l` for the control file after cloning or provisioning a repo.
- Use per-repo copies of control files instead of a shared symlinked config.
When it happens
Trigger: Creating `ln -s ~/.gitnexusrc-global .gitnexusrc` in the repo, symlinked fixture files in a test checkout, package managers or dotfile managers (stow, chezmoi) that replace config files with symlinks, then running analyze with strict config loading.
Common situations: Dotfile management setups symlinking .gitnexusrc from a home repo; sharing one config across repos via symlink; build/test scripts linking fixture configs into the repo.
Related errors
- ${source} entry "${trimmed}" must be an identifier or member
- ${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic li
- ${filename} resolves outside the repository root
- ${filename} must be a regular file
- path must include owner/repo without traversal
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-09-01).
Data as JSON: /api/errors/fecac40072d19ad6.
Report an issue: GitHub.