garrytan/gstack · warning · Error
Usage: browse header <name>:<value>
Error message
Usage: browse header <name>:<value>
What it means
Thrown by `browse header` when the first argument is missing or does not contain a `:` character. The command splits on the first `:` to derive header name and value; without a colon it cannot determine the boundary and rejects the input. This is the header counterpart to the cookie command's `=` requirement.
Source
Thrown at browse/src/write-commands.ts:573
case 'cookie': {
const cookieStr = args[0];
if (!cookieStr || !cookieStr.includes('=')) throw new Error('Usage: browse cookie <name>=<value>');
const eq = cookieStr.indexOf('=');
const name = cookieStr.slice(0, eq);
const value = cookieStr.slice(eq + 1);
const url = new URL(page.url());
await page.context().addCookies([{
name,
value,
domain: url.hostname,
path: '/',
}]);
return `Cookie set: ${name}=****`;
}
case 'header': {
const headerStr = args[0];
if (!headerStr || !headerStr.includes(':')) throw new Error('Usage: browse header <name>:<value>');
const sep = headerStr.indexOf(':');
const name = headerStr.slice(0, sep).trim();
const value = headerStr.slice(sep + 1).trim();
await bm.setExtraHeader(name, value);
const sensitiveHeaders = ['authorization', 'cookie', 'set-cookie', 'x-api-key', 'x-auth-token'];
const redactedValue = sensitiveHeaders.includes(name.toLowerCase()) ? '****' : value;
return `Header set: ${name}: ${redactedValue}`;
}
case 'useragent': {
const ua = args.join(' ');
if (!ua) throw new Error('Usage: browse useragent <string>');
bm.setUserAgent(ua);
const error = await bm.recreateContext();
if (error) {
return `User agent set to "${ua}" but: ${error}`;
}
return `User agent set: ${ua}`;View on GitHub (pinned to 94993f7401)
Solutions
- Provide the token as `name:value`: `browse header X-Custom:foo`.
- Only the FIRST colon is the separator, so values containing colons (URLs, times) are fine: `browse header Referer:https://example.com`.
- If you meant to set a cookie, use `browse cookie name=value`.
- Sensitive header names (authorization, cookie, set-cookie, x-api-key, x-auth-token) are redacted to `****` in the success message — the value is still applied, only the echo is masked.
Example fix
// before await runBrowseCommand(['header', 'X-Custom=foo']); // after await runBrowseCommand(['header', 'X-Custom:foo']);
Defensive patterns
Strategy: validation
Validate before calling
function parseHeaderToken(token: string): { name: string; value: string } {
if (!token || !token.includes(':')) {
throw new Error('header token must be name:value');
}
const sep = token.indexOf(':');
return { name: token.slice(0, sep).trim(), value: token.slice(sep + 1).trim() };
} Type guard
function isHeaderToken(s: string): boolean {
return typeof s === 'string' && s.includes(':');
} Prevention
- Use ':' as the separator for headers (the first ':' is the split point, so URL values work).
- Do not confuse with the cookie command which uses '='.
- Sensitive header names are redacted in the success echo but still applied.
When it happens
Trigger: Calling `browse header` with no args; `browse header X-Custom` (value omitted); `browse header X-Custom=foo` (used `=` instead of `:`); a value that legitimately contains no colon because the user passed only the name.
Common situations: User conflates header (`:`) and cookie (`=`) syntax; an agent copies a fetch options object (`{ 'X-Custom': 'foo' }`) and emits `X-Custom foo` losing the separator; setting a header whose value contains a URL (`Referer: https://...`) works because only the first `:` is the split point — the user incorrectly assumes the URL's `://` breaks parsing and pre-trims.
Related errors
- Usage: browse viewport [<WxH>] [--scale <n>] (e.g. 375x812,
- Usage: browse cookie <name>=<value>
- Usage: browse useragent <string>
- Usage: browse upload <selector> <file1> [file2...]
- Usage: browse cookie-import <json-file>
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/f0800c8bc015fcb2.
Report an issue: GitHub.