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

  1. Drop the positional selector and keep only `--selector <css>`.
  2. Or drop the `--selector` flag and keep the positional `@ref`/CSS.
  3. 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

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


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