jackwener/OpenCLI · error · Error

Invalid --wait-for selector "${kwargs['wait-for']}": ${waitR

Error message

Invalid --wait-for selector "${kwargs['wait-for']}": ${waitResult.error || 'querySelector failed'}

What it means

The web read command's --wait-for option runs buildWaitForSelectorAcrossFramesJs, which waits up to waitSeconds for a CSS selector to appear in the main document or same-origin iframes. If the result flags invalidSelector, the command throws Error with the selector, the underlying querySelector error, or a generic 'querySelector failed' fallback — meaning the --wait-for value is not a syntactically valid CSS selector, not a timeout.

Source

Thrown at clis/web/read.js:429

        { name: 'stdout', type: 'boolean', default: false, help: 'Print markdown to stdout instead of saving to a file' },
    ],
    columns: ['title', 'author', 'publish_time', 'status', 'size', 'saved'],
    func: async (page, kwargs, debug = false) => {
        const url = kwargs.url;
        const waitSeconds = kwargs.wait ?? 3;
        const waitUntil = normalizeWaitUntil(kwargs['wait-until']);
        const frameMode = normalizeFrameMode(kwargs.frames);
        const shouldDiagnose = boolish(kwargs.diagnose) || debug || !!process.env.OPENCLI_VERBOSE;
        const networkEntries = [];
        const captureSupported = (waitUntil === 'networkidle' || shouldDiagnose)
            ? await maybeStartNetworkCapture(page)
            : false;
        // Navigate to the target URL
        await page.goto(url);
        if (kwargs['wait-for']) {
            const waitResult = await page.evaluate(buildWaitForSelectorAcrossFramesJs(String(kwargs['wait-for']), waitSeconds * 1000));
            if (waitResult?.invalidSelector) {
                throw new Error(`Invalid --wait-for selector "${kwargs['wait-for']}": ${waitResult.error || 'querySelector failed'}`);
            }
            if (!waitResult?.ok) {
                throw new Error(`Timed out waiting for selector "${kwargs['wait-for']}" in main document or same-origin iframes`);
            }
        } else if (waitUntil !== 'networkidle') {
            await page.wait(waitSeconds);
        }
        if (waitUntil === 'networkidle') {
            if (!captureSupported) {
                throw new Error('Network capture is unavailable, so --wait-until networkidle cannot be satisfied');
            }
            const idle = await waitForNetworkIdle(page, waitSeconds, networkEntries);
            if (!idle?.ok) {
                throw new Error(`Timed out waiting for network idle after ${waitSeconds}s`);
            }
        }
        // Extract article content using browser-side heuristics
        const data = await page.evaluate(buildRenderAwareExtractorJs({ frames: frameMode }));

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Correct the --wait-for value to a valid CSS selector, e.g. --wait-for "#main .content" instead of "//div" or prose text.
  2. Test the selector in the browser DevTools console with document.querySelector('<selector>') to confirm it parses.
  3. Escape quotes/brackets properly for your shell so the selector reaches the CLI intact.
  4. Replace unsupported selectors (XPath, jQuery extensions like :contains) with standard CSS equivalents.

Example fix

// before
$ opencli web read https://example.com --wait-for "//div[@id='app']"
// Error: Invalid --wait-for selector "//div[@id='app']": querySelector failed
// after (valid CSS selector)
$ opencli web read https://example.com --wait-for "div#app"
Defensive patterns

Strategy: validation

Validate before calling

const sel = kwargs['wait-for'];
if (sel) {
  try { document.createDocumentFragment().querySelector(sel); }
  catch (e) { throw new Error(`--wait-for is not a valid CSS selector: ${sel}`); }
}

Type guard

function isValidCssSelector(sel) {
  if (typeof sel !== 'string' || !sel.trim()) return false;
  try { document.createDocumentFragment().querySelector(sel); return true; }
  catch { return false; }
}

Try / catch

try {
  await readPage(url, { waitFor: sel });
} catch (e) {
  if (/Invalid --wait-for selector/.test(e.message)) {
    // fix the selector to valid CSS (no XPath, no prose) and retry
  } else throw e;
}

Prevention

When it happens

Trigger: Passing --wait-for with a malformed CSS selector (unbalanced brackets/quotes, invalid pseudo-class like :contains, plain text instead of a selector, or an XPath expression) so page.evaluate returns {invalidSelector:true, error:...}.

Common situations: Users pass text like "Login button" instead of a CSS selector; use XPath syntax ('//div[@id]') which querySelector doesn't accept; typo'd pseudo-selectors unsupported by the browser engine; shell escaping mangles quotes/brackets in the selector.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/9b4782e06287d7d8. Report an issue: GitHub.