jackwener/OpenCLI · error · CommandExecutionError
search type failed
Error message
search type failed
What it means
Thrown by the qoder search command when the fill script — which finds the last visible text/search input, sets its value via the native setter, and dispatches input/change events — fails. fillRes.ok being false usually means no visible input was found in the open search palette. Without the query being typed, results never appear.
Source
Thrown at clis/qoder/ui.js:92
const query = String(kwargs?.query || '').trim();
if (!query) throw new ArgumentError('query', 'is required');
const limit = parsePositiveInt(kwargs?.limit, 20, '--limit');
const openRes = await evaluateQoder(page, clickByTextScript(['Search']));
if (!openRes?.ok) throw new CommandExecutionError(openRes?.reason || 'search open failed', '');
await page.wait(0.5);
// Type into the visible input (usually the most-recently mounted one).
const fillRes = await evaluateQoder(page, `(() => {
${IS_VISIBLE_JS}
const inputs = Array.from(document.querySelectorAll('input[type="text"], input[type="search"], input:not([type])')).filter(isVisible);
const input = inputs[inputs.length - 1];
if (!input) return { ok: false, reason: 'No input visible.' };
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(input, ${JSON.stringify(query)});
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
return { ok: true };
})()`);
if (!fillRes?.ok) throw new CommandExecutionError(fillRes?.reason || 'search type failed', '');
await page.wait(0.8);
const items = requireArrayResult(await evaluateQoder(page, `(() => {
${IS_VISIBLE_JS}
// Search palette results: prefer [role=option], else any clickable row in the modal.
const opts = Array.from(document.querySelectorAll('[role="option"], [role="menuitem"]')).filter(isVisible);
const titles = opts.map((o) => {
// Codex-style fix: text may be in per-char spans; use textContent.
const titleEl = o.querySelector('.truncate, [class*="title"i]') || o;
return (titleEl.textContent || '').trim().replace(/\\s+/g, ' ').slice(0, 200);
}).filter(Boolean);
return [...new Set(titles)];
})()`), 'qoder search');
try { await page.evaluate(`document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));`); } catch {}
if (!items.length) {
throw new EmptyResultError('qoder search', `No items matched "${query}".`);
}
return items.slice(0, limit).map((t, i) => ({ Index: i + 1, Item: t }));
},View on GitHub (pinned to 49907e53dc)
Solutions
- Add a short wait after opening the palette so the input finishes mounting/animating in.
- Extend the input selector list to cover contenteditable and role=textbox: '[contenteditable="true"], [role="textbox"]'.
- Verify the palette is open before typing (check for the modal element).
- Update selectors to match the current Qoder search input markup.
Example fix
// before
const inputs = Array.from(document.querySelectorAll('input[type="text"], input[type="search"], input:not([type])')).filter(isVisible);
// after
await page.wait(0.3);
const inputs = Array.from(document.querySelectorAll('input[type="text"], input[type="search"], input:not([type]), [role="textbox"], [contenteditable="true"]')).filter(isVisible); Defensive patterns
Strategy: retry
Validate before calling
const paletteOpen = await page.evaluate(`!!document.querySelector('[role="dialog"], [class*="palette"], [class*="modal"]')`);
if (!paletteOpen) throw new Error('Search palette did not open; cannot type'); Type guard
function foundVisibleInput(sel) { return Boolean(document.querySelector(sel) && document.querySelector(sel).offsetParent !== null); } Try / catch
try {
await search(page, query);
} catch (e) {
if (/search type failed/.test(String(e.message))) {
await page.wait(1);
return search(page, query);
}
throw e;
} Prevention
- Wait for the palette animation to finish before typing.
- Include contenteditable/[role=textbox] in the input selector list.
- Verify the palette is open before running the fill script.
- Escape and reopen the palette if a previous attempt left it in a bad state.
When it happens
Trigger: The search palette opened but contains no input matching input[type="text"], input[type="search"], or input without type; the palette uses a contenteditable or custom editor instead of <input>; the input is not visible (display:none during animation); the palette didn't actually open.
Common situations: Qoder update switching to a custom combobox component; palette mount animation making the input temporarily invisible when the script runs; headless rendering differences; palette closed by an earlier Escape/blur.
Related errors
- 找不到消息输入框
- Could not find an add-to-cart button on the product page.
- Instagram action button not found: ${labels.join(' / ')}
- sidebar-toggle failed
- open-panel failed
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/bfeccadfe677148c.
Report an issue: GitHub.