garrytan/gstack · error · Error

File not found: ${filePath}

Error message

File not found: ${filePath}

What it means

Thrown by `browse cookie-import` when the path passes the safe-directory check but `fs.existsSync(filePath)` returns false. The existence check runs AFTER the security check by design — security is validated on the resolved path even for non-existent files, so a probing caller cannot learn whether arbitrary paths exist. This ordering prevents information leakage about the filesystem outside the safe roots.

Source

Thrown at browse/src/write-commands.ts:657

      return 'Dialogs will be dismissed';
    }

    case 'cookie-import': {
      const filePath = args[0];
      if (!filePath) throw new Error('Usage: browse cookie-import <json-file>');
      // Path validation — resolve to absolute and check against safe dirs.
      // Fixes #707: relative paths previously bypassed the safe directory check.
      // Mirrors validateOutputPath() — resolves symlinks (e.g., macOS /tmp → /private/tmp).
      const resolved = path.resolve(filePath);
      let resolvedReal = resolved;
      try { resolvedReal = fs.realpathSync(resolved); } catch {
        // File may not exist yet — resolve parent dir instead
        try { resolvedReal = path.join(fs.realpathSync(path.dirname(resolved)), path.basename(resolved)); } catch {}
      }
      if (!SAFE_DIRECTORIES.some(dir => isPathWithin(resolvedReal, dir))) {
        throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);
      }
      if (!fs.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
      const raw = fs.readFileSync(filePath, 'utf-8');
      let cookies: any[];
      try { cookies = JSON.parse(raw); } catch (err: any) { throw new Error(`Invalid JSON in ${filePath}: ${err?.message || err}`); }
      if (!Array.isArray(cookies)) throw new Error('Cookie file must contain a JSON array');

      // Auto-fill domain from current page URL when missing (consistent with cookie command)
      const pageUrl = new URL(page.url());
      const defaultDomain = pageUrl.hostname;

      for (const c of cookies) {
        if (!c.name || c.value === undefined) throw new Error('Each cookie must have "name" and "value" fields');
        if (!c.domain) {
          c.domain = defaultDomain;
        } else {
          const cookieDomain = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;
          if (cookieDomain !== defaultDomain && !defaultDomain.endsWith('.' + cookieDomain)) {
            throw new Error(`Cookie domain "${c.domain}" does not match current page domain "${defaultDomain}". Use the target site first.`);
          }

View on GitHub (pinned to 94993f7401)

Solutions

  1. Verify the file exists from the browse server's process: `fs.existsSync(path.resolve(filePath))`.
  2. If the file was in TEMP_DIR, it may have been reaped — re-export and re-import in the same session.
  3. Expand `~` to `os.homedir()` (and ensure homedir is inside a safe directory, or stage to TEMP_DIR).
  4. Pass an absolute path to avoid cwd ambiguity.

Example fix

// before
await runBrowseCommand(['cookie-import', '~/cookies.json']);

// after
import fs from 'fs';
import os from 'os';
import path from 'path';
const staged = path.join(os.tmpdir(), 'cookies.json');
fs.copyFileSync(path.join(os.homedir(), 'cookies.json'), staged);
await runBrowseCommand(['cookie-import', staged]);
Defensive patterns

Strategy: validation

Validate before calling

import fs from 'fs';
import path from 'path';
function ensureImportFileExists(filePath: string): void {
  if (!fs.existsSync(filePath)) {
    throw new Error(`File not found: ${filePath}`);
  }
}

Type guard

function fileExists(p: string): boolean {
  try { return fs.statSync(p).isFile(); } catch { return false; }
}

Prevention

When it happens

Trigger: The JSON file was deleted between the safe-directory check and the existence check; a typo in the filename; a relative path that does not resolve against cwd; an unexpanded `~`; the file lives in TEMP_DIR but under a different name.

Common situations: Agent exported cookies to a temp file, the temp dir was cleaned (cron tmpwatch, container restart), and the import runs later; user passes a relative path expecting it to resolve against their shell's cwd but the browse server has a different cwd; macOS case-insensitivity masked a casing error.

Related errors


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