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
- Verify the file exists from the browse server's process: `fs.existsSync(path.resolve(filePath))`.
- If the file was in TEMP_DIR, it may have been reaped — re-export and re-import in the same session.
- Expand `~` to `os.homedir()` (and ensure homedir is inside a safe directory, or stage to TEMP_DIR).
- 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
- Use absolute paths resolved from the browse server cwd.
- Re-export cookies to TEMP_DIR if the prior file was reaped by tmp cleanup.
- Expand '~' to os.homedir() (and stage into a safe dir if homedir is not allowed).
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
- File not found: ${fp}
- Usage: browse cookie-import <json-file>
- Invalid JSON in ${filePath}: ${err?.message || err}
- Each cookie must have "name" and "value" fields
- Cookie domain "${c.domain}" does not match current page doma
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/4e387bfe3f2260db.
Report an issue: GitHub.