garrytan/gstack · error · Error
Invalid state file: expected cookies and pages arrays
Error message
Invalid state file: expected cookies and pages arrays
What it means
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).
Source
Thrown at browse/src/meta-commands.ts:957
if (action === 'save') {
const state = await bm.saveState();
// V1: cookies + URLs only (not localStorage — breaks on load-before-navigate)
const saveData = {
version: 1,
savedAt: new Date().toISOString(),
cookies: state.cookies,
pages: state.pages.map(p => ({ url: p.url, isActive: p.isActive })),
};
writeSecureFile(statePath, JSON.stringify(saveData, null, 2));
return `State saved: ${statePath} (${state.cookies.length} cookies, ${state.pages.length} pages)\n⚠️ Cookies stored in plaintext. Delete when no longer needed.`;
}
if (action === 'load') {
if (!fs.existsSync(statePath)) throw new Error(`State not found: ${statePath}`);
const data = JSON.parse(fs.readFileSync(statePath, 'utf-8'));
if (!Array.isArray(data.cookies) || !Array.isArray(data.pages)) {
throw new Error('Invalid state file: expected cookies and pages arrays');
}
// Validate and filter cookies — reject malformed or internal-network cookies
const validatedCookies = data.cookies.filter((c: any) => {
if (typeof c !== 'object' || !c) return false;
if (typeof c.name !== 'string' || typeof c.value !== 'string') return false;
if (typeof c.domain !== 'string' || !c.domain) return false;
const d = c.domain.startsWith('.') ? c.domain.slice(1) : c.domain;
if (d === 'localhost' || d.endsWith('.internal') || d === '169.254.169.254') return false;
return true;
});
if (validatedCookies.length < data.cookies.length) {
console.warn(`[browse] Filtered ${data.cookies.length - validatedCookies.length} invalid cookies from state file`);
}
// Warn on state files older than 7 days
if (data.savedAt) {
const ageMs = Date.now() - new Date(data.savedAt).getTime();
const SEVEN_DAYS = 7 * 24 * 60 * 60 * 1000;
if (ageMs > SEVEN_DAYS) {View on GitHub (pinned to 94993f7401)
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.
Example fix
// before (file content)
{ "session": { "cookies": [], "pages": [] } }
// after
{ "version": 1, "savedAt": "...", "cookies": [], "pages": [] } Defensive patterns
Strategy: type-guard
Validate before calling
const data = JSON.parse(raw);
if (!Array.isArray(data?.cookies) || !Array.isArray(data?.pages)) {
throw new Error('State file schema mismatch: expected { cookies: [], pages: [] }');
} Type guard
const isStateFile = (d: unknown): d is { cookies: unknown[]; pages: unknown[] } =>
typeof d === 'object' && d !== null &&
Array.isArray((d as any).cookies) && Array.isArray((d as any).pages); Try / catch
try { await browse.state('load', name); }
catch (err) {
if (/Invalid state file/.test(err.message)) {
// re-save a fresh state and discard the corrupt file
}
} Prevention
- Treat saved state as opaque — don't hand-edit.
- On schema bumps, migrate files explicitly or invalidate old versions via the `version` field.
When it happens
Trigger: Loading a hand-edited JSON, a file from a different tool/version, a truncated write, or a future schema where `cookies`/`pages` was renamed.
Common situations: Manual edits to a saved state file, cross-version migration (the `version: 1` schema changed), or disk corruption / partial writes.
Related errors
- State name must be alphanumeric (a-z, 0-9, _, -)
- Skill name is empty.
- Invalid skill name "${name}". Must be lowercase letters/digi
- stageSkill: files map is empty.
- Invalid file path in stageSkill: "${relPath}".
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/dc9f1fa9b88c6840.
Report an issue: GitHub.