garrytan/gstack · warning · Error
Usage: browse viewport [<WxH>] [--scale <n>] (e.g., 375x812)
Error message
Usage: browse viewport [<WxH>] [--scale <n>] (e.g., 375x812)
What it means
Thrown by `browse viewport` when a size argument IS supplied but does not contain the `x` width×height separator. The parser reaches the size-resolution block, calls `sizeArg.includes('x')`, and on `false` rejects the value because it cannot split it into width and height. The accepted form is `<W>x<H>` such as `375x812`.
Source
Thrown at browse/src/write-commands.ts:532
}
scaleArg = parsed;
} else if (args[i].startsWith('--')) {
throw new Error(`Unknown viewport flag: ${args[i]}`);
} else if (sizeArg === undefined) {
sizeArg = args[i];
} else {
throw new Error(`Unexpected positional arg: ${args[i]}. Usage: viewport [WxH] [--scale <n>]`);
}
}
if (sizeArg === undefined && scaleArg === undefined) {
throw new Error('Usage: browse viewport [<WxH>] [--scale <n>] (e.g. 375x812, or --scale 2 to keep current size)');
}
// Resolve width/height: either from sizeArg or from current viewport if --scale-only.
let w: number, h: number;
if (sizeArg) {
if (!sizeArg.includes('x')) throw new Error('Usage: browse viewport [<WxH>] [--scale <n>] (e.g., 375x812)');
const [rawW, rawH] = sizeArg.split('x').map(Number);
w = Math.min(Math.max(Math.round(rawW) || 1280, 1), 16384);
h = Math.min(Math.max(Math.round(rawH) || 720, 1), 16384);
} else {
// --scale without WxH → use BrowserManager's tracked viewport (source of truth
// since setViewport + launchContext keep it in sync). Falls back reliably on
// headed → headless transitions or contexts with viewport:null.
const current = bm.getCurrentViewport();
w = current.width;
h = current.height;
}
if (scaleArg !== undefined) {
const err = await bm.setDeviceScaleFactor(scaleArg, w, h);
if (err) return `Viewport partially set: ${err}`;
return `Viewport set to ${w}x${h} @ ${scaleArg}x (context recreated; refs and load-html content replayed)`;
}
View on GitHub (pinned to 94993f7401)
Solutions
- Use a lowercase `x` with no surrounding spaces: `browse viewport 375x812`.
- If you copied the value, re-type the separator manually to avoid smart quotes or U+00D7.
- Provide both dimensions; the command does not accept a single dimension.
- If you meant to scale rather than resize, switch to `browse viewport --scale <n>`.
Example fix
// before await runBrowseCommand(['viewport', '375X812']); // after await runBrowseCommand(['viewport', '375x812']);
Defensive patterns
Strategy: validation
Validate before calling
function parseViewportSize(arg: string): { w: number; h: number } {
const m = /^(\d+)x(\d+)$/i.exec(arg.trim());
if (!m) throw new Error(`Expected <W>x<H> (e.g. 375x812), got: ${arg}`);
return { w: Number(m[1]), h: Number(m[2]) };
} Type guard
function isViewportSizeToken(s: string): boolean {
return /^\d+x\d+$/i.test(s);
} Prevention
- Use a lowercase ASCII 'x' as the width/height separator.
- Re-type the separator when copy-pasting resolutions to avoid smart quotes or U+00D7.
- Provide both dimensions; single-dimension forms are unsupported.
When it happens
Trigger: Passing `browse viewport 375X812` (capital X), `browse viewport 375*812`, `browse viewport 1080` (single dimension), `browse viewport 375,812`, or `browse viewport 375x` (trailing x with no height, which `split('x').map(Number)` would yield NaN for and is better rejected explicitly).
Common situations: User copies a resolution from a spec written as `375 × 812` or `375*812`; an agent emits dimensions using the wrong separator it inferred from the page; locale-specific number formatting where a user tries `1080x1920` but a smart-quote or non-ASCII `x` (U+00D7 multiply sign) slips in.
Related errors
- Usage: browse viewport [<WxH>] [--scale <n>] (e.g. 375x812,
- Usage: browse cookie <name>=<value>
- Usage: browse header <name>:<value>
- Usage: browse useragent <string>
- Usage: browse upload <selector> <file1> [file2...]
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/6c584e6a6640beb8.
Report an issue: GitHub.