garrytan/gstack · error · Error
Usage: echo '[["goto","url"],["text"]]' | browse chain or
Error message
Usage: echo '[["goto","url"],["text"]]' | browse chain or: browse chain 'goto url | click @e5 | snapshot -ic'
What it means
The `chain` meta-command expects a single argument that is either a JSON array of command arrays (`[["goto","url"],["text"]]`) or a pipe-delimited script (`goto url | click @e5 | snapshot -ic`). When `args[0]` is empty/undefined the command cannot infer what to run, so it throws this usage message (lines 600-604).
Source
Thrown at browse/src/meta-commands.ts:601
validateOutputPath(screenshotPath);
await page.screenshot({ path: screenshotPath, fullPage: true });
await guardScreenshotPath(screenshotPath);
results.push(`${vp.name} (${vp.width}x${vp.height}): ${screenshotPath}`);
}
// Restore original viewport
if (originalViewport) {
await page.setViewportSize(originalViewport);
}
return results.join('\n');
}
// ─── Chain ─────────────────────────────────────────
case 'chain': {
// Read JSON array from args[0] (if provided) or expect it was passed as body
const jsonStr = args[0];
if (!jsonStr) throw new Error(
'Usage: echo \'[["goto","url"],["text"]]\' | browse chain\n' +
' or: browse chain \'goto url | click @e5 | snapshot -ic\''
);
let rawCommands: string[][];
try {
rawCommands = JSON.parse(jsonStr);
if (!Array.isArray(rawCommands)) throw new Error('not array');
} catch (err: any) {
// Fallback: pipe-delimited format "goto url | click @e5 | snapshot -ic"
if (!(err instanceof SyntaxError) && err?.message !== 'not array') throw err;
rawCommands = jsonStr.split(' | ')
.filter(seg => seg.trim().length > 0)
.map(seg => tokenizePipeSegment(seg.trim()));
}
// Canonicalize aliases across the whole chain. Pair canonical name with the raw
// input so result labels + error messages reflect what the user typed, but everyView on GitHub (pinned to 94993f7401)
Solutions
- Pass a JSON array as the first arg: `browse chain '[["goto","https://x"],["text"]]'`.
- Or pass a pipe-delimited script: `browse chain 'goto https://x | text'`.
- If piping via stdin, make sure the server/CLI forwards stdin into `args[0]` — otherwise inline the payload.
Example fix
// before browse chain // after browse chain '[["goto","https://example.com"],["text"]]'
Defensive patterns
Strategy: validation
Validate before calling
if (!args[0] || typeof args[0] !== 'string') {
throw new Error('chain requires a JSON array or pipe-delimited script as args[0]');
} Type guard
const isChainPayload = (s: string): boolean => {
try { return Array.isArray(JSON.parse(s)); }
catch { return s.includes('|') || /\s/.test(s); }
}; Try / catch
try { await browse.chain(payload); }
catch (err) {
if (/Usage:/.test(err.message) && /chain/.test(err.message)) {
// rebuild payload as JSON array of [cmd, ...args]
}
} Prevention
- Always construct chain payloads as `JSON.stringify([[cmd, ...args], ...])`.
- Validate the payload is a non-empty string before dispatch.
- Unit-test the JSON shape in your wrapper.
When it happens
Trigger: Calling `browse chain` with no arguments, piping nothing on stdin when the server expected `args[0]`, or passing an empty string.
Common situations: First-time users forgetting the JSON shape; shell quoting that swallowed the argument; an agent constructing the call without a payload.
Related errors
- pdf: --from-file requires a path
- Unknown pdf flag: ${a}
- pdf: --${flag} requires a value
- Usage: screenshot --selector <css> [path]
- Usage: screenshot --clip x,y,w,h [path]
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/7686871f791de0c6.
Report an issue: GitHub.