{"record":{"id":"8315ce3be84a3962","repo":"jackwener/OpenCLI","slug":"search-open-failed","errorCode":null,"errorMessage":"search open failed","messagePattern":"search open failed","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/qoder/ui.js","lineNumber":78,"sourceCode":"cli({\n    site: 'qoder',\n    name: 'search',\n    access: 'read',\n    description: 'Open Qoder Search palette (⌘P), type a query, return matched options.',\n    domain: 'localhost',\n    strategy: Strategy.UI,\n    browser: true,\n    args: [\n        { name: 'query', positional: true, required: true, help: 'Search text' },\n        { name: 'limit', type: 'int', required: false, default: 20 },\n    ],\n    columns: ['Index', 'Item'],\n    func: async (page, kwargs) => {\n        const query = String(kwargs?.query || '').trim();\n        if (!query) throw new ArgumentError('query', 'is required');\n        const limit = parsePositiveInt(kwargs?.limit, 20, '--limit');\n        const openRes = await evaluateQoder(page, clickByTextScript(['Search']));\n        if (!openRes?.ok) throw new CommandExecutionError(openRes?.reason || 'search open failed', '');\n        await page.wait(0.5);\n        // Type into the visible input (usually the most-recently mounted one).\n        const fillRes = await evaluateQoder(page, `(() => {\n      ${IS_VISIBLE_JS}\n      const inputs = Array.from(document.querySelectorAll('input[type=\"text\"], input[type=\"search\"], input:not([type])')).filter(isVisible);\n      const input = inputs[inputs.length - 1];\n      if (!input) return { ok: false, reason: 'No input visible.' };\n      const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;\n      setter.call(input, ${JSON.stringify(query)});\n      input.dispatchEvent(new Event('input', { bubbles: true }));\n      input.dispatchEvent(new Event('change', { bubbles: true }));\n      return { ok: true };\n    })()`);\n        if (!fillRes?.ok) throw new CommandExecutionError(fillRes?.reason || 'search type failed', '');\n        await page.wait(0.8);\n        const items = requireArrayResult(await evaluateQoder(page, `(() => {\n      ${IS_VISIBLE_JS}\n      // Search palette results: prefer [role=option], else any clickable row in the modal.","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/qoder/ui.js#L60-L96","documentation":"Thrown by the qoder search command when the initial click on the 'Search' text (to open the search palette) fails. clickByTextScript found no visible 'Search' element, so evaluateQoder returned ok:false and the command raises CommandExecutionError with reason or the 'search open failed' fallback. The rest of the search flow (typing, reading results) cannot proceed without the palette open.","triggerScenarios":"No visible element containing 'Search' text exists (icon-only search control, different label); the search feature is unavailable on the current page; the page is still loading; Qoder update renamed the search entry point; the keyboard shortcut is the only way to open search.","commonSituations":"Qoder redesign replacing the text-labeled search button with an icon; localized UI; user not on the main quest view; slow load in headless mode.","solutions":["Confirm the search control's actual text/aria-label in the UI and update clickByTextScript.","Use clickFirstScript with selectors like input[type=\"search\"] or [aria-label=\"Search\"] as a fallback.","Add a wait for the page/search control to be present before clicking.","Alternatively trigger the search keyboard shortcut (e.g. Ctrl/Cmd+K) via keydown dispatch if text click keeps failing."],"exampleFix":"// before\nconst openRes = await evaluateQoder(page, clickByTextScript(['Search']));\n// after\nlet openRes = await evaluateQoder(page, clickByTextScript(['Search']));\nif (!openRes?.ok) openRes = await evaluateQoder(page, clickFirstScript(['[aria-label=\"Search\"]', 'input[type=\"search\"]']));\nif (!openRes?.ok) throw new CommandExecutionError(openRes?.reason || 'search open failed', '');","handlingStrategy":"fallback","validationCode":"const hasSearch = await page.evaluate(`Array.from(document.querySelectorAll('button, [role=\"button\"]')).some(el => /search/i.test(el.textContent || el.getAttribute('aria-label') || ''))`);\nif (!hasSearch) console.warn('No search control found on page');","typeGuard":"function isClickOk(res) { return Boolean(res && res.ok === true); }","tryCatchPattern":"try {\n  await search(page, query);\n} catch (e) {\n  if (/search open failed/.test(String(e.message))) {\n    await page.evaluate(`document.dispatchEvent(new KeyboardEvent('keydown', { key: 'k', ctrlKey: true, bubbles: true }))`);\n    return searchFromOpenPalette(page, query);\n  }\n  throw e;\n}","preventionTips":["Use the keyboard shortcut (Cmd/Ctrl+K) as an alternate open path.","Update search-entry selectors after Qoder updates.","Ensure the page is on the main quest view where search exists.","Wait for the search control to be visible before clicking."],"tags":["ui-automation","dom-selector","click-failed"],"backgroundTag":"selector-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}