garrytan/gstack · warning · Error
Cannot use load-html inside a frame. Run 'frame main' first.
Error message
Cannot use load-html inside a frame. Run 'frame main' first.
What it means
Frame-context guard in the load-html case at write-commands.ts:181. setContent operates on the top-level page; with a subframe selected it is refused for the same reason as goto/back/forward/reload — page-level operations cannot run against a frame-scoped session.
Source
Thrown at browse/src/write-commands.ts:179
return `Back → ${page.url()}`;
}
case 'forward': {
if (inFrame) throw new Error('Cannot use forward inside a frame. Run \'frame main\' first.');
session.clearLoadedHtml();
await page.goForward({ waitUntil: 'domcontentloaded', timeout: 15000 });
return `Forward → ${page.url()}`;
}
case 'reload': {
if (inFrame) throw new Error('Cannot use reload inside a frame. Run \'frame main\' first.');
session.clearLoadedHtml();
await page.reload({ waitUntil: 'domcontentloaded', timeout: 15000 });
return `Reloaded ${page.url()}`;
}
case 'load-html': {
if (inFrame) throw new Error('Cannot use load-html inside a frame. Run \'frame main\' first.');
// --from-file <path.json>: read inline HTML from a JSON payload. Used by
// make-pdf to dodge Windows argv size limits on large rendered HTML.
// The JSON shape is { html: string, waitUntil?: "load"|"domcontentloaded"|"networkidle" }.
// The safe-dirs + magic-byte + size-cap checks below still apply to the
// INLINE HTML content, not to the payload file path itself.
let fromFilePayload: { html: string; waitUntil?: SetContentWaitUntil } | null = null;
let filePath: string | undefined;
let waitUntil: SetContentWaitUntil = 'domcontentloaded';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--from-file') {
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., anView on GitHub (pinned to 94993f7401)
Solutions
- Run 'frame main' before 'load-html'
- Load HTML fixtures at the start of the flow, before any frame entry
- Guard on session.getFrame() === null
Example fix
// before
await handleWriteCommand('load-html', ['--from-file','p.json'], session, bm) // in frame → throws
// after
await handleWriteCommand('frame', ['main'], session, bm)
await handleWriteCommand('load-html', ['--from-file','p.json'], session, bm) Defensive patterns
Strategy: validation
Validate before calling
function canNavigate(session: { getFrame(): unknown }): boolean {
return session.getFrame() === null
} Type guard
function isMainFrameSession(s: { getFrame(): unknown }): boolean {
return s.getFrame() === null
} Prevention
- Run 'frame main' before 'load-html'
- Load fixtures before entering any frame
- Assert getFrame() === null
When it happens
Trigger: Running 'load-html' (either the file path or --from-file form) while a frame context is active.
Common situations: Loading fixture HTML after a prior step entered an iframe and never reset to main.
Related errors
- Cannot use goto inside a frame. Run 'frame main' first.
- Cannot use back inside a frame. Run 'frame main' first.
- Cannot use forward inside a frame. Run 'frame main' first.
- Cannot use reload inside a frame. Run 'frame main' first.
- load-html: --from-file requires a path
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/310146288794d59e.
Report an issue: GitHub.