abhigyanpatwari/GitNexus · error · Error

auto-sync clone root is not a directory: ${root}

Error message

auto-sync clone root is not a directory: ${root}

What it means

Thrown by assertDirectoryOwnerAndPermissions when fs.stat() on the clone root succeeds but stat.isDirectory() is false — the configured path exists as a regular file, socket, device node, or similar non-directory entry. Auto-sync needs a directory to clone repositories into, so it refuses to proceed rather than overwriting or writing alongside the existing file.

Source

Thrown at gitnexus/src/core/auto-sync/path-security.ts:260

  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;
  const groupWritable = (mode & 0o020) !== 0;
  const worldWritable = (mode & 0o002) !== 0;
  if (worldWritable) {
    throw new Error(`Refusing world-writable auto-sync clone root: ${root}`);
  }
  if (groupWritable) {

View on GitHub (pinned to 0d1aed942f)

Solutions

  1. Remove or relocate the file currently occupying the clone-root path, then let auto-sync (re)create the directory
  2. Point the configuration at an existing directory
  3. Check for a stray lock file, log file, or typo that caused the path to be created as a file
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at gitnexus/src/core/auto-sync/path-security.ts:260 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/afeac48e75951bd8. Report an issue: GitHub.