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
- Correct the --wait-for value to a valid CSS selector, e.g. --wait-for "#main .content" instead of "//div" or prose text.
- Test the selector in the browser DevTools console with document.querySelector('<selector>') to confirm it parses.
- Escape quotes/brackets properly for your shell so the selector reaches the CLI intact.
- 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
- Validate the selector with document.querySelector in DevTools before passing it to --wait-for.
- Use only standard CSS selectors — no XPath ('//div') or jQuery-only pseudo-classes like :contains.
- Quote/escape the selector for your shell so brackets and quotes survive parsing.
- Distinguish this from the timeout error: 'Invalid selector' means syntax, 'Timed out' means the element never appeared.
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
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Waiting for 12306 tk auth cookie
- <train-no> must not be empty
- amazon.com
- Unexpected Amazon probe: ${JSON.stringify(probe)}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/9b4782e06287d7d8.
Report an issue: GitHub.