garrytan/gstack · error · Error
load-html: --from-file JSON parse failed: ${e.message}
Error message
load-html: --from-file JSON parse failed: ${e.message} What it means
Parse guard at write-commands.ts:209. The file at payloadPath passed validateReadPath and was read as UTF-8, but JSON.parse(raw) threw. The --from-file contract is a JSON wrapper {html, waitUntil?}, not raw HTML or any other format.
Source
Thrown at browse/src/write-commands.ts:209
const payloadPath = args[++i];
if (!payloadPath) throw new Error('load-html: --from-file requires a path');
// Parity with the sibling `load-html <file>` path below (line 249):
// that branch runs every `file://` target through validateReadPath
// so the safe-dirs policy can't be side-stepped. Same policy must
// apply here — otherwise --from-file becomes a read-anywhere escape
// hatch for any caller that can pick the payload path (e.g., an
// MCP caller issuing load-html with an attacker-influenced path).
try {
validateReadPath(path.resolve(payloadPath));
} catch {
throw new Error(
`load-html: --from-file ${payloadPath} must be under ${SAFE_DIRECTORIES.join(' or ')} (security policy). Copy the payload into the project tree or /tmp first.`
);
}
const raw = fs.readFileSync(payloadPath, 'utf8');
let json: any;
try { json = JSON.parse(raw); }
catch (e: any) { throw new Error(`load-html: --from-file JSON parse failed: ${e.message}`); }
if (typeof json.html !== 'string') {
throw new Error('load-html: --from-file JSON must have a "html" string field');
}
if (json.waitUntil && json.waitUntil !== 'load'
&& json.waitUntil !== 'domcontentloaded' && json.waitUntil !== 'networkidle') {
throw new Error(`load-html: --from-file waitUntil '${json.waitUntil}' invalid`);
}
fromFilePayload = { html: json.html, waitUntil: json.waitUntil };
} else if (args[i] === '--wait-until') {
const val = args[++i];
if (val !== 'load' && val !== 'domcontentloaded' && val !== 'networkidle') {
throw new Error(`Invalid --wait-until '${val}'. Must be one of: load, domcontentloaded, networkidle.`);
}
waitUntil = val;
} else if (args[i].startsWith('--')) {
throw new Error(`Unknown flag: ${args[i]}`);
} else if (!filePath) {
filePath = args[i];View on GitHub (pinned to 94993f7401)
Solutions
- Wrap the HTML in a JSON object: {"html": "<your html>"}
- Validate with a JSON linter: jq . payload.json
- For a raw HTML file, use 'load-html <file>' (the file-path branch) instead of --from-file
- Strip BOM and re-save as UTF-8 without BOM
Example fix
// before — raw HTML passed to --from-file (fails JSON.parse)
await handleWriteCommand('load-html', ['--from-file','page.html'], session, bm)
// after — either wrap in JSON, or use the file path branch
await handleWriteCommand('load-html', ['page.html'], session, bm) Defensive patterns
Strategy: try-catch
Validate before calling
function isJsonFile(p: string): boolean {
try { JSON.parse(fs.readFileSync(p, 'utf8')); return true } catch { return false }
} Try / catch
let json: any
try { json = JSON.parse(raw) }
catch (e) {
// fall back to the raw-HTML file path branch instead of --from-file
await handleWriteCommand('load-html', [payloadPath], session, bm)
return
} Prevention
- Use --from-file only for the {html, waitUntil?} JSON envelope
- For raw .html files, use 'load-html <file>' instead
- Validate payloads with a JSON linter before loading
When it happens
Trigger: Pointing --from-file at: a raw .html file; plaintext; a JSON file with trailing commas, single quotes, or comments; a file with a leading BOM; binary/corrupted content.
Common situations: Passing an .html file to --from-file instead of the JSON envelope; copy-paste introducing smart quotes or truncation; CRLF/BOM from a Windows editor.
Related errors
- load-html: --from-file JSON must have a "html" string field
- Cannot use load-html inside a frame. Run 'frame main' first.
- load-html: --from-file requires a path
- load-html: --from-file ${payloadPath} must be under ${SAFE_D
- load-html: --from-file waitUntil '${json.waitUntil}' invalid
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/13ae01ad7c76c96c.
Report an issue: GitHub.