abhigyanpatwari/GitNexus · error
${filename} must be a regular file
Error message
${filename} must be a regular file What it means
readRepoControlFile validates repository control files (e.g. .gitnexus config files) before reading them to prevent symlink/hardlink-based tampering. Before opening, it lstats the path and rejects anything that is not a regular file. This guard ensures the library never reads content from devices, directories, FIFOs, or other special file types.
Source
Thrown at gitnexus/src/config/repo-control-file.ts:22
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 => {
if (settled) return;View on GitHub (pinned to 52924ef12c)
Solutions
- Check the path with `fs.lstatSync(path).isFile()` and remove/replace the non-regular entry.
- If a directory was created by mistake, remove it (`rm -r`) and create the expected regular file.
- Verify the configured filename/path passed to loadAnalyzeConfigStrict points at the intended regular file.
- Re-run the command; the file is re-validated on every read.
Example fix
// before: path is a directory/FIFO rm -rf .gitnexusrc // after: write a regular file printf 'mode: strict\n' > .gitnexusrc
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
const st = fs.lstatSync(controlFilePath);
if (!st.isFile()) {
throw new Error(`${controlFilePath} is not a regular file; remove it and create a plain file`);
} Type guard
function isRegularFile(p: string): boolean {
try { return fs.lstatSync(p).isFile(); } catch { return false; }
} Try / catch
try {
const content = await readRepoControlFile(root, filename);
} catch (err) {
if ((err as Error).message.includes('must be a regular file')) {
fs.rmSync(path.join(root, filename), { recursive: true, force: true });
// recreate or skip
} else throw err;
} Prevention
- Never create directories, FIFOs, or device nodes at control-file paths.
- Add a preflight `lstat` check in scripts that generate the config.
- Keep control files in version control so they are always plain regular files.
When it happens
Trigger: Calling readRepoControlFile (directly or via loadAnalyzeConfigStrict / content) when the target path exists but lstat reports a non-regular type: a directory, FIFO, socket, device node, or symlink (symlink has its own message).
Common situations: A developer creates a directory where the control file is expected (e.g. mkdir .gitnexusrc by mistake); a misconfigured path points at /dev/stdin or a named pipe in CI; a provisioning script left a socket or fifo at the config path.
Related errors
- ${source} entry "${trimmed}" must be an identifier or member
- ${filename} resolves outside the repository root
- ${filename} exceeds ${MAX_REPO_CONTROL_FILE_BYTES} bytes
- path must include owner/repo without traversal
- argument contains NUL/CR/LF, unsafe for the Windows shell: $
AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-09-01).
Data as JSON: /api/errors/41e6529177a4fd71.
Report an issue: GitHub.