garrytan/gstack · error · Error

Cannot resolve path: ${filePath}

Error message

Cannot resolve path: ${filePath}

What it means

Thrown by validateTempPath when realpathSync fails with an errno other than ENOENT (e.g., EACCES). The file may exist but the browse process cannot resolve its real path due to permissions or a broken symlink in TEMP_DIR, so the raw 'Cannot resolve path' error is surfaced.

Source

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

    }
  }
  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');
    }
    throw new Error(`Cannot resolve path: ${filePath}`);
  }
  const isSafe = TEMP_ONLY.some(dir => isPathWithin(realPath, dir));
  if (!isSafe) {
    throw new Error(`Path must be within: ${TEMP_ONLY.join(', ')} (remote file serving is restricted to temp directory)`);
  }
}

/** Escape special regex metacharacters in a user-supplied string to prevent ReDoS. */
export function escapeRegExp(s: string): string {
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

View on GitHub (pinned to 94993f7401)

Solutions

  1. Check permissions on the file and every component of the path: `namei -l <path>`
  2. Remove broken symlinks in TEMP_DIR: `find <TEMP_DIR> -xtype l -delete`
  3. Regenerate the file so it is owned by the current browse process
  4. If the file is from another user, copy it into a fresh temp path owned by this process
Defensive patterns

Strategy: try-catch

Validate before calling

import * as fs from 'fs';

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

// Distinguish ENOENT (caught as 'File not found') from other errors
function tempPathStatus(p: string): 'ok' | 'missing' | 'unresolvable' {
  try { fs.realpathSync(p); return 'ok'; }
  catch (e: any) {
    if (e.code === 'ENOENT') return 'missing';
    return 'unresolvable';
  }
}

Type guard

function isReadableTempFile(p: string): boolean {
  try { fs.realpathSync(p); fs.accessSync(p, fs.constants.R_OK); return true; } catch { return false; }
}

Try / catch

try {
  return serveTempFile(filePath);
} catch (e: any) {
  if (/Cannot resolve path/.test(e.message)) {
    return { status: 500, body: 'Temp file exists but cannot be resolved (permissions or broken symlink)' };
  }
  throw e;
}

Prevention

When it happens

Trigger: Requesting a temp file whose path cannot be resolved: permission denied on a path component, a broken symlink in TEMP_DIR, or an I/O error reading the directory.

Common situations: Permission mismatch between the browse process and the file owner (file written by a different user); broken symlink leftover in TEMP_DIR; filesystem corruption or transient I/O error.

Related errors


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