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

  1. Add a short wait after opening the palette so the input finishes mounting/animating in.
  2. Extend the input selector list to cover contenteditable and role=textbox: '[contenteditable="true"], [role="textbox"]'.
  3. Verify the palette is open before typing (check for the modal element).
  4. 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

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


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