{"record":{"id":"dc9f1fa9b88c6840","repo":"garrytan/gstack","slug":"invalid-state-file-expected-cookies-and-pages-arr","errorCode":null,"errorMessage":"Invalid state file: expected cookies and pages arrays","messagePattern":"Invalid state file: expected cookies and pages arrays","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/meta-commands.ts","lineNumber":957,"sourceCode":"\n      if (action === 'save') {\n        const state = await bm.saveState();\n        // V1: cookies + URLs only (not localStorage — breaks on load-before-navigate)\n        const saveData = {\n          version: 1,\n          savedAt: new Date().toISOString(),\n          cookies: state.cookies,\n          pages: state.pages.map(p => ({ url: p.url, isActive: p.isActive })),\n        };\n        writeSecureFile(statePath, JSON.stringify(saveData, null, 2));\n        return `State saved: ${statePath} (${state.cookies.length} cookies, ${state.pages.length} pages)\\n⚠️  Cookies stored in plaintext. Delete when no longer needed.`;\n      }\n\n      if (action === 'load') {\n        if (!fs.existsSync(statePath)) throw new Error(`State not found: ${statePath}`);\n        const data = JSON.parse(fs.readFileSync(statePath, 'utf-8'));\n        if (!Array.isArray(data.cookies) || !Array.isArray(data.pages)) {\n          throw new Error('Invalid state file: expected cookies and pages arrays');\n        }\n        // Validate and filter cookies — reject malformed or internal-network cookies\n        const validatedCookies = data.cookies.filter((c: any) => {\n          if (typeof c !== 'object' || !c) return false;\n          if (typeof c.name !== 'string' || typeof c.value !== 'string') return false;\n          if (typeof c.domain !== 'string' || !c.domain) return false;\n          const d = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;\n          if (d === 'localhost' || d.endsWith('.internal') || d === '169.254.169.254') return false;\n          return true;\n        });\n        if (validatedCookies.length < data.cookies.length) {\n          console.warn(`[browse] Filtered ${data.cookies.length - validatedCookies.length} invalid cookies from state file`);\n        }\n        // Warn on state files older than 7 days\n        if (data.savedAt) {\n          const ageMs = Date.now() - new Date(data.savedAt).getTime();\n          const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000;\n          if (ageMs > SEVEN_DAYS) {","sourceCodeStart":939,"sourceCodeEnd":975,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/meta-commands.ts#L939-L975","documentation":"After reading `<name>.json`, the loader validates that both `data.cookies` and `data.pages` are arrays (line 957). If either is missing or not an array the file is rejected as structurally invalid — partial restoration would silently drop cookies or tabs. Subsequent cookie entries are validated separately (lines 959-967).","triggerScenarios":"Loading a hand-edited JSON, a file from a different tool/version, a truncated write, or a future schema where `cookies`/`pages` was renamed.","commonSituations":"Manual edits to a saved state file, cross-version migration (the `version: 1` schema changed), or disk corruption / partial writes.","solutions":["Re-save a fresh state with `browse state save <name>` and load that.","If migrating from another format, transform the JSON to `{ cookies: [...], pages: [...] }` before loading.","Discard the corrupt file and recreate the session manually."],"exampleFix":"// before (file content)\n{ \"session\": { \"cookies\": [], \"pages\": [] } }\n// after\n{ \"version\": 1, \"savedAt\": \"...\", \"cookies\": [], \"pages\": [] }","handlingStrategy":"type-guard","validationCode":"const data = JSON.parse(raw);\nif (!Array.isArray(data?.cookies) || !Array.isArray(data?.pages)) {\n  throw new Error('State file schema mismatch: expected { cookies: [], pages: [] }');\n}","typeGuard":"const isStateFile = (d: unknown): d is { cookies: unknown[]; pages: unknown[] } =>\n  typeof d === 'object' && d !== null &&\n  Array.isArray((d as any).cookies) && Array.isArray((d as any).pages);","tryCatchPattern":"try { await browse.state('load', name); }\ncatch (err) {\n  if (/Invalid state file/.test(err.message)) {\n    // re-save a fresh state and discard the corrupt file\n  }\n}","preventionTips":["Treat saved state as opaque — don't hand-edit.","On schema bumps, migrate files explicitly or invalidate old versions via the `version` field."],"tags":["state","validation","json-schema"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}