abhigyanpatwari/GitNexus · error

${filename} resolves outside the repository root

Error message

${filename} resolves outside the repository root

What it means

Thrown by readRepoControlFile in repo-control-file.ts when the requested filename, resolved against the repo root, escapes that root: path.relative() yields a path starting with '..' or an absolute path. Repo control files (like .gitnexusrc) are only read from inside the repository to prevent path traversal and reading files outside the project.

Source

Thrown at gitnexus/src/config/repo-control-file.ts:15

import fs from 'node:fs';
import * as path from 'node:path';

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,
      });

View on GitHub (pinned to 52924ef12c)

Solutions

  1. Place the control file inside the repository root and reference it by a root-relative name, e.g. '.gitnexusrc'.
  2. Remove any '../' or absolute-path components from the filename argument.
  3. Ensure the process runs with the correct repo root so path.resolve(repoRoot, filename) stays inside it.
  4. If you need config from another location, copy/symlink-check it into the repo (a plain in-repo file, since symlinks are rejected separately).

Example fix

// before
await readRepoControlFile(repoRoot, '../shared/.gitnexusrc');

// after
cp('../shared/.gitnexusrc', path.join(repoRoot, '.gitnexusrc'));
await readRepoControlFile(repoRoot, '.gitnexusrc');
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
export function assertInsideRepo(repoRoot: string, filename: string): void {
  const root = path.resolve(repoRoot);
  const rel = path.relative(root, path.resolve(root, filename));
  if (rel.startsWith('..') || path.isAbsolute(rel)) {
    throw new Error(`${filename} resolves outside the repository root`);
  }
}

Try / catch

try {
  const cfg = await readRepoControlFile(repoRoot, filename);
} catch (e) {
  if (e.message.includes('resolves outside the repository root')) {
    console.error(`Control file must live inside ${repoRoot}; got ${filename}`);
    process.exitCode = 1;
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a filename such as '../other/repo/.gitnexusrc', '/etc/passwd', an absolute path outside the root, or a symlink-resolving name whose lexical resolution escapes the root to readRepoControlFile/loadAnalyzeConfigStrict.

Common situations: Building the config path with untrusted user input, misconfigured working directory so path.resolve lands outside the repo, test harnesses pointing at fixture files outside the repo root, or ../-relative path templates.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@52924ef12c (2026-09-01). Data as JSON: /api/errors/ec6e552e3d277e24. Report an issue: GitHub.