{"record":{"id":"0e1f6fc4783b0084","repo":"jackwener/OpenCLI","slug":"click-on-account-button-label-failed","errorCode":null,"errorMessage":"Click on account button \"${label}\" failed","messagePattern":"Click on account button \"(.+?)\" failed","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/qoder/ui.js","lineNumber":278,"sourceCode":"            label = await evaluateQoder(page, `(() => {\n        ${IS_VISIBLE_JS}\n        const btns = Array.from(document.querySelectorAll('button')).filter(isVisible);\n        const known = new Set(['Pin', 'Copy', 'New Quest', 'Search', 'Settings', 'View all', 'Knowledge', 'Marketplace', 'Credits Usage', 'Open Editor', 'Add Workspace', 'More Actions', 'Send message', 'Prompt Enhance', 'Voice input', 'button']);\n        const candidate = btns.find((b) => {\n          const tx = (b.innerText || '').trim();\n          if (!tx || tx.length > 30 || tx.includes('⌘') || known.has(tx)) return false;\n          if (/^(New|Open|Add|Save|Edit|Cancel|OK|Close)/.test(tx)) return false;\n          return true;\n        });\n        return candidate ? (candidate.innerText || '').trim() : '';\n      })()`);\n        }\n        if (!label) {\n            throw new CommandExecutionError('No account button detected. Pass --username <name> explicitly.', '');\n        }\n        // Click the username button\n        const clickRes = await evaluateQoder(page, clickByTextScript([label], { exact: true, maxLen: 60 }));\n        if (!clickRes?.ok) throw new CommandExecutionError(`Click on account button \"${label}\" failed`, '');\n        await page.wait(0.5);\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*=\"dropdown\"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('button, [role=\"menuitem\"], a'))\n        .filter(isVisible)\n        .map((b) => (b.innerText || b.textContent || '').trim().replace(/\\\\s+/g, ' '))\n        .filter(Boolean);\n    })()`), 'qoder account');\n        try { await page.evaluate(`document.body.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));`); } catch {}\n        const rows = [{ Field: 'Username', Value: label }];\n        (items || []).slice(0, 20).forEach((item, i) => {\n            rows.push({ Field: `Item[${i + 1}]`, Value: item });\n        });\n        return rows;","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/qoder/ui.js#L260-L296","documentation":"In the Qoder 'account' command, after detecting (or being given) the username label, the adapter clicks it with clickByTextScript([label], {exact:true, maxLen:60}). If that click reports {ok:false}, it throws CommandExecutionError(`Click on account button \"${label}\" failed`). Detection succeeded but the exact-text click could not find/click the matching button.","triggerScenarios":"The label scraped from a candidate button does not exactly equal any clickable element's trimmed innerText (detection and click use different matching: detection is heuristic, click uses exact match with maxLen 60); username longer than 60 chars; whitespace/emoji differences in the rendered text; UI re-rendered between detection and click.","commonSituations":"Username containing trailing whitespace, newlines, or an avatar glyph so exact match fails; long display names truncated in UI but passed in full via --username; Qoder re-render replacing the element between the two evaluate calls.","solutions":["Pass a --username value exactly as displayed in the UI (same case, no truncation), or shorten to the displayed form.","Fall back to substring matching (exact:false) when exact click fails.","Re-detect and click in one pass to avoid re-render races.","Increase maxLen if the display name is longer than 60 characters.","Inspect the button's innerText via CDP DevTools and use that exact string."],"exampleFix":"// before\nclickByTextScript([label], { exact: true, maxLen: 60 })\n// after: fallback to substring match\nlet clickRes = await evaluateQoder(page, clickByTextScript([label], { exact: true, maxLen: 60 }));\nif (!clickRes?.ok) clickRes = await evaluateQoder(page, clickByTextScript([label], { exact: false, maxLen: 120 }));","handlingStrategy":"validation","validationCode":"// Ensure the username matches the exact UI text before clicking\nfunction normalizeUsername(name) {\n  const s = String(name).replace(/\\s+/g, ' ').trim();\n  if (!s || s.length > 60) {\n    throw new Error('Username must be non-empty and <= 60 chars, exactly as shown in the Qoder UI.');\n  }\n  return s;\n}","typeGuard":"function isValidUsernameLabel(label) {\n  return typeof label === 'string' && label.trim().length > 0 && label.length <= 60;\n}","tryCatchPattern":"try {\n  await cli.run(['qoder', 'account', '--username', label]);\n} catch (e) {\n  if (String(e.message).includes('Click on account button')) {\n    // retry with the exact innerText copied from the Qoder UI\n    console.error('Use the exact display name as rendered (whitespace/emoji included).');\n  } else throw e;\n}","preventionTips":["Copy the --username value exactly from the rendered button text","Avoid passing truncated or full-length names when the UI truncates them","Whitespace-normalize usernames before passing them","If auto-detected, prefer using the same detection path end-to-end"],"tags":["ui-automation","electron","exact-match","selector-not-found"],"backgroundTag":"ui-element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}