{"record":{"id":"6de008ca2470e7e9","repo":"garrytan/gstack","slug":"invalid-json-in-filepath-err-message-err","errorCode":null,"errorMessage":"Invalid JSON in ${filePath}: ${err?.message || err}","messagePattern":"Invalid JSON in (.+?): (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/write-commands.ts","lineNumber":660,"sourceCode":"    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          }\n        }\n        if (!c.path) c.path = '/';\n      }","sourceCodeStart":642,"sourceCodeEnd":678,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/write-commands.ts#L642-L678","documentation":"Thrown by `browse cookie-import` when `JSON.parse(raw)` throws on the file contents. The original parse error is captured and re-thrown with the file path prepended, preserving the underlying message (e.g. `Unexpected token < in JSON at position 0`). The file is read as UTF-8 text first, so encoding issues also surface here.","triggerScenarios":"The file is actually HTML (a login page saved by mistake), a Netscape-format `cookies.txt` (not JSON), a JSON file with a trailing comma, a BOM-prefixed file, a file that was concatenated with a second JSON document, or a binary/empty file.","commonSituations":"User ran a `curl` that saved an error page instead of the cookie JSON; an export tool wrote Netscape format instead of JSON; a hand-edited JSON file has a trailing comma or single quotes; the file was corrupted by a partial write during a crash.","solutions":["Validate the file parses as JSON before importing: `JSON.parse(fs.readFileSync(fp, 'utf-8'))` in a scratch script.","If the file is Netscape format, convert it to a JSON array of cookie objects first.","Strip a leading BOM: `raw.charCodeAt(0) === 0xFEFF` then `raw.slice(1)`.","Re-export the cookies with a tool that emits JSON (Playwright's `context.cookies()` output is the expected shape)."],"exampleFix":"// before\n// file contains: name=value; name2=value2  (NOT JSON)\nawait runBrowseCommand(['cookie-import', fp]);\n\n// after\n// write a proper JSON array\nconst cookies = [{ name: 'name', value: 'value', domain: 'example.com', path: '/' }];\nfs.writeFileSync(fp, JSON.stringify(cookies));\nawait runBrowseCommand(['cookie-import', fp]);","handlingStrategy":"try-catch","validationCode":"import fs from 'fs';\nfunction validateCookieJsonFile(filePath: string): any[] {\n  let raw = fs.readFileSync(filePath, 'utf-8');\n  if (raw.charCodeAt(0) === 0xFEFF) raw = raw.slice(1); // strip BOM\n  const parsed = JSON.parse(raw); // throws on invalid\n  if (!Array.isArray(parsed)) throw new Error('cookie file must contain a JSON array');\n  return parsed;\n}","typeGuard":"function isCookieArray(v: unknown): v is unknown[] {\n  return Array.isArray(v);\n}","tryCatchPattern":"try {\n  const cookies = JSON.parse(raw);\n} catch (err: any) {\n  throw new Error(`Invalid JSON in ${filePath}: ${err?.message || err}`);\n}","preventionTips":["Validate the file parses as JSON in a scratch step before importing.","Strip a leading UTF-8 BOM before parsing.","Use Playwright context.cookies() export format as the canonical shape."],"tags":["json","cookies","import","parse-error","browse-command"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}