{"record":{"id":"4e387bfe3f2260db","repo":"garrytan/gstack","slug":"file-not-found-filepath-4e387b","errorCode":null,"errorMessage":"File not found: ${filePath}","messagePattern":"File not found: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/write-commands.ts","lineNumber":657,"sourceCode":"      return 'Dialogs will be dismissed';\n    }\n\n    case 'cookie-import': {\n      const filePath = args[0];\n      if (!filePath) throw new Error('Usage: browse cookie-import <json-file>');\n      // Path validation — resolve to absolute and check against safe dirs.\n      // Fixes #707: relative paths previously bypassed the safe directory check.\n      // Mirrors validateOutputPath() — resolves symlinks (e.g., macOS /tmp → /private/tmp).\n      const resolved = path.resolve(filePath);\n      let resolvedReal = resolved;\n      try { resolvedReal = fs.realpathSync(resolved); } catch {\n        // File may not exist yet — resolve parent dir instead\n        try { resolvedReal = path.join(fs.realpathSync(path.dirname(resolved)), path.basename(resolved)); } catch {}\n      }\n      if (!SAFE_DIRECTORIES.some(dir => isPathWithin(resolvedReal, dir))) {\n        throw new Error(`Path must be within: ${SAFE_DIRECTORIES.join(', ')}`);\n      }\n      if (!fs.existsSync(filePath)) throw new Error(`File not found: ${filePath}`);\n      const raw = fs.readFileSync(filePath, 'utf-8');\n      let cookies: any[];\n      try { cookies = JSON.parse(raw); } catch (err: any) { throw new Error(`Invalid JSON in ${filePath}: ${err?.message || err}`); }\n      if (!Array.isArray(cookies)) throw new Error('Cookie file must contain a JSON array');\n\n      // Auto-fill domain from current page URL when missing (consistent with cookie command)\n      const pageUrl = new URL(page.url());\n      const defaultDomain = pageUrl.hostname;\n\n      for (const c of cookies) {\n        if (!c.name || c.value === undefined) throw new Error('Each cookie must have \"name\" and \"value\" fields');\n        if (!c.domain) {\n          c.domain = defaultDomain;\n        } else {\n          const cookieDomain = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;\n          if (cookieDomain !== defaultDomain && !defaultDomain.endsWith('.' + cookieDomain)) {\n            throw new Error(`Cookie domain \"${c.domain}\" does not match current page domain \"${defaultDomain}\". Use the target site first.`);\n          }","sourceCodeStart":639,"sourceCodeEnd":675,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L639-L675","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nawait runBrowseCommand(['cookie-import', '~/cookies.json']);\n\n// after\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nconst staged = path.join(os.tmpdir(), 'cookies.json');\nfs.copyFileSync(path.join(os.homedir(), 'cookies.json'), staged);\nawait runBrowseCommand(['cookie-import', staged]);","handlingStrategy":"validation","validationCode":"import fs from 'fs';\nimport path from 'path';\nfunction ensureImportFileExists(filePath: string): void {\n  if (!fs.existsSync(filePath)) {\n    throw new Error(`File not found: ${filePath}`);\n  }\n}","typeGuard":"function fileExists(p: string): boolean {\n  try { return fs.statSync(p).isFile(); } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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)."],"tags":["filesystem","cookies","import","file-not-found","browse-command"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}