{"record":{"id":"75c988253f84692a","repo":"jackwener/OpenCLI","slug":"qoder-more-actions","errorCode":null,"errorMessage":"qoder more-actions","messagePattern":"qoder more-actions","errorType":"exception","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/qoder/ui.js","lineNumber":328,"sourceCode":"    columns: ['Index', 'Item'],\n    func: async (page) => {\n        const res = await evaluateQoder(page, clickByTextScript(['More Actions']));\n        if (!res?.ok) throw new CommandExecutionError(res?.reason || 'More Actions button not found', '');\n        await page.wait(0.4);\n        const items = requireArrayResult(await evaluateQoder(page, `(() => {\n      ${IS_VISIBLE_JS}\n      const popovers = Array.from(document.querySelectorAll('[role=\"menu\"], [role=\"dialog\"], [class*=\"popover\"i], [class*=\"menu\"i]')).filter(isVisible)\n        .filter((el) => { const r = el.getBoundingClientRect(); return r.width < 500 && r.height < 600; });\n      if (!popovers.length) return [];\n      const pop = popovers[popovers.length - 1];\n      return Array.from(pop.querySelectorAll('[role=\"menuitem\"], button'))\n        .filter(isVisible)\n        .map((b) => (b.innerText || b.textContent || '').trim().replace(/\\\\s+/g, ' '))\n        .filter(Boolean);\n    })()`), 'qoder more-actions');\n        try { await page.evaluate(`document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));`); } catch {}\n        if (!items.length) {\n            throw new EmptyResultError('qoder more-actions', 'Menu opened but no items detected.');\n        }\n        return items.map((it, i) => ({ Index: i + 1, Item: it }));\n    },\n});\n","sourceCodeStart":310,"sourceCodeEnd":333,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/qoder/ui.js#L310-L333","documentation":"After opening the More Actions menu, the Qoder 'more-actions' command extracts menu items and passes them to requireArrayResult with the label 'qoder more-actions'. If page.evaluate returns a non-array (unexpected result shape, e.g. the script errored or the CDP payload was wrapped unexpectedly), requireArrayResult throws CommandExecutionError('qoder more-actions: unexpected evaluate result shape'). The bracketed message 'qoder more-actions' is that label prefix.","triggerScenarios":"The menu-scraping IIFE throws inside the page (evaluate returns an error object instead of an array), the CDP evaluate wrapper returns {session,data} with data not an array, or the page navigated/closed mid-evaluate.","commonSituations":"Menu closed before the scrape ran (Escape/focus change), causing a script branch returning undefined; Qoder window closed between click and scrape; CDP session detached; a Qoder update changed DOM so the query throws inside the page context.","solutions":["Ensure the menu stays open until the scrape completes (avoid moving focus/clicking elsewhere) and retry.","Keep the Qoder window open and attached over CDP for the whole command.","Wrap the in-page script in try/catch returning [] so evaluate always yields an array, then surface 'no items' via EmptyResultError instead.","Check that unwrapEvaluateResult handles the payload shape your CDP layer returns.","Re-run the command; transient focus races are the usual cause."],"exampleFix":"// before\nconst items = requireArrayResult(await evaluateQoder(page, scrapeScript), 'qoder more-actions');\n// after: tolerate non-array and report empty\nconst raw = await evaluateQoder(page, `(() => { try { ...scrape...; return list; } catch (e) { return []; } })()`);\nconst items = Array.isArray(raw) ? raw : [];","handlingStrategy":"try-catch","validationCode":"async function menuStillOpen(page) {\n  const open = await page.evaluate(`(() =>\n    !!document.querySelector('[role=\"menu\"]:not([hidden])')\n  )()`);\n  if (open !== true) throw new Error('More Actions menu already closed — re-open before scraping.');\n}","typeGuard":"function isMenuItemArray(value) {\n  return Array.isArray(value) && value.every((v) => typeof v === 'string');\n}","tryCatchPattern":"try {\n  const items = await cli.run(['qoder', 'more-actions']);\n} catch (e) {\n  if (String(e.message).includes('qoder more-actions')) {\n    // menu raced closed or evaluate returned a non-array; retry once after reopening\n    await cli.run(['qoder', 'more-actions']);\n  } else throw e;\n}","preventionTips":["Do not interact with the window (click/Escape) between opening the menu and scraping","Keep the Qoder window open and CDP attached for the whole command","Wrap in-page scrape scripts in try/catch returning [] so failures surface as empty results, not shape errors","Retry once on transient focus races before failing"],"tags":["ui-automation","cdp","unexpected-result-shape"],"backgroundTag":"unexpected-evaluate-result-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}