garrytan/gstack · error · Error
--selector conflicts with positional selector — choose one
Error message
--selector conflicts with positional selector — choose one
What it means
Thrown when the `screenshot` command receives both the `--selector <css>` flag AND a positional selector (a token starting with `@e`/`@c`/`.`/`#` or containing `[`). The parser forbids two competing target specifiers because the resulting capture would be ambiguous. Only one element target is allowed.
Source
Thrown at browse/src/meta-commands.ts:485
}
// Separate target (selector/@ref) from output path
for (const arg of remaining) {
// File paths containing / and ending with an image/pdf extension are never CSS selectors
const isFilePath = arg.includes('/') && /\.(png|jpe?g|webp|pdf)$/i.test(arg);
if (isFilePath) {
outputPath = arg;
} else if (arg.startsWith('@e') || arg.startsWith('@c') || arg.startsWith('.') || arg.startsWith('#') || arg.includes('[')) {
targetSelector = arg;
} else {
outputPath = arg;
}
}
// --selector flag takes precedence; conflict with positional selector.
if (flagSelector !== undefined) {
if (targetSelector !== undefined) {
throw new Error('--selector conflicts with positional selector — choose one');
}
targetSelector = flagSelector;
}
validateOutputPath(outputPath);
if (clipRect && targetSelector) {
throw new Error('Cannot use --clip with a selector/ref — choose one');
}
if (viewportOnly && clipRect) {
throw new Error('Cannot use --viewport with --clip — choose one');
}
// --base64 mode: capture to buffer instead of disk
if (base64Mode) {
let buffer: Buffer;
if (targetSelector) {
const resolved = await bm.resolveRef(targetSelector);View on GitHub (pinned to 94993f7401)
Solutions
- Drop the positional selector and keep only `--selector <css>`.
- Or drop the `--selector` flag and keep the positional `@ref`/CSS.
- If the second token was meant to be the output path, give it a `/` and an image extension (e.g. `out.png`) so the path-classifier (lines 471-472) treats it as a file, not a selector.
Example fix
// before browse screenshot --selector .hero @e3 out.png // after browse screenshot --selector .hero out.png
Defensive patterns
Strategy: validation
Validate before calling
const hasSelectorFlag = args.includes('--selector');
const positionalSelector = args.find(a =>
/^(@e|@c|\.|#|\[)/.test(a));
if (hasSelectorFlag && positionalSelector) {
throw new Error('Pass either --selector or a positional selector, not both');
} Type guard
const looksLikeSelector = (s: string): boolean => /^(?:@e|@c|\.|#|\[)/.test(s);
Try / catch
try { await browse.screenshot(args); }
catch (err) {
if (/conflicts with positional selector/.test(err.message)) {
args = args.filter(a => !looksLikeSelector(a) || a === flagSelector);
}
} Prevention
- Standardize on ONE way to pass selectors in your wrapper (flag OR positional).
- Programmatically construct args from a single source of truth to avoid drift.
When it happens
Trigger: Calling `browse screenshot --selector .hero @e3` or `browse screenshot --selector #nav .menu` — i.e. passing a CSS/ref both via the flag and positionally (lines 482-486).
Common situations: Refactoring a call from positional to flag form and forgetting to delete the old token, or templates/macros that concatenate a selector into both slots.
Related errors
- Cannot use --clip with a selector/ref — choose one
- Cannot use --viewport with --clip — choose one
- Usage: screenshot --selector <css> [path]
- Usage: screenshot --clip x,y,w,h [path]
- Unknown screenshot flag: ${args[i]}
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/880a2ee6f66c86a7.
Report an issue: GitHub.