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

  1. Use a lowercase `x` with no surrounding spaces: `browse viewport 375x812`.
  2. If you copied the value, re-type the separator manually to avoid smart quotes or U+00D7.
  3. Provide both dimensions; the command does not accept a single dimension.
  4. 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

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


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/6c584e6a6640beb8. Report an issue: GitHub.