garrytan/gstack · error · Error

Cannot resolve real path: ${filePath} (${err.code})

Error message

Cannot resolve real path: ${filePath} (${err.code})

What it means

Thrown by validateReadPath when realpathSync fails with an errno other than ENOENT. ENOENT is handled separately (file does not exist yet, fall back to parent-dir check); any other failure — EACCES (permission denied), ELOOP (circular symlink), ENOTDIR (a path component is not a directory) — surfaces here with the raw errno code.

Source

Thrown at browse/src/path-security.ts:92

  }
}

/** Validate a file path for reading (eval command). */
export function validateReadPath(filePath: string): void {
  const resolved = path.resolve(filePath);
  let realPath: string;
  try {
    realPath = fs.realpathSync(resolved);
  } catch (err: any) {
    if (err.code === 'ENOENT') {
      try {
        const dir = fs.realpathSync(path.dirname(resolved));
        realPath = path.join(dir, path.basename(resolved));
      } catch {
        realPath = resolved;
      }
    } else {
      throw new Error(`Cannot resolve real path: ${filePath} (${err.code})`);
    }
  }
  const isSafe = SAFE_DIRECTORIES.some(dir => isPathWithin(realPath, dir));
  if (!isSafe) {
    throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);
  }
}

/** Validate a file path for remote serving (GET /file). TEMP_DIR only, not cwd. */
export function validateTempPath(filePath: string): void {
  const resolved = path.resolve(filePath);
  let realPath: string;
  try {
    realPath = fs.realpathSync(resolved);
  } catch (err: any) {
    if (err.code === 'ENOENT') {
      throw new Error('File not found');
    }

View on GitHub (pinned to 94993f7401)

Solutions

  1. Inspect permissions: `ls -la <path>` and `namei -l <path>` to see per-component perms
  2. chmod/chown the file or run the browse process as an authorized user
  3. Break symlink loops: `readlink -f <path>` to find the cycle, then remove the offending link
  4. Ensure every component except the last is a directory
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from 'fs';

function canReadResolved(p: string): boolean {
  try {
    fs.realpathSync(p);
    fs.accessSync(p, fs.constants.R_OK);
    return true;
  } catch (e: any) {
    if (e.code === 'ENOENT') return true; // non-existent is fine for read validation
    return false; // EACCES, ELOOP, ENOTDIR, etc.
  }
}

if (!canReadResolved(filePath)) {
  throw new Error(`Cannot read ${filePath}: fix permissions or symlink loops first`);
}

Type guard

function isResolvableRealpath(p: string): boolean {
  try { fs.realpathSync(p); return true; }
  catch (e: any) { return e.code === 'ENOENT'; }
}

Try / catch

try {
  await runReadCommand(filePath);
} catch (e: any) {
  if (/Cannot resolve real path/.test(e.message)) {
    const m = e.message.match(/\((\w+)\)$/);
    const code = m?.[1];
    if (code === 'EACCES') console.error('Permission denied — chmod/chown the file or run as an authorized user.');
    else if (code === 'ELOOP') console.error('Circular symlink — remove the loop with readlink -f.');
    else if (code === 'ENOTDIR') console.error('A path component is not a directory.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling eval with a file path that hits a permission wall, a symlink loop, or a non-directory component. realpathSync throws and err.code is not 'ENOENT', so the original cause is re-exposed with its errno.

Common situations: File owned by another user with no read permission on the browse process; symlink loop created by misconfigured dotfiles; a regular file sitting where a directory was expected in the path; SELinux/AppArmor denial.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/5168a343e4576cb3. Report an issue: GitHub.