{"record":{"id":"3af66e24521c94c4","repo":"jackwener/OpenCLI","slug":"send-button-not-found","errorCode":null,"errorMessage":"Send button not found","messagePattern":"Send button not found","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/qoder/quest.js","lineNumber":78,"sourceCode":"    columns: ['Status', 'Length'],\n    func: async (page, kwargs) => {\n        const text = String(kwargs?.text || '').trim();\n        if (!text) throw new ArgumentError('text is required');\n\n        const beforeCount = await evaluateQoder(page, QODER_MESSAGE_COUNT_JS);\n        const typeRes = await evaluateQoder(page, buildQoderInjectTextScript(text));\n        if (!typeRes?.ok) throw new CommandExecutionError(typeRes?.reason || 'composer type failed', '');\n        await page.wait(0.3);\n\n        // Click Send message\n        const sendRes = await evaluateQoder(page, clickFirstScript([\n            'button[aria-label=\"Send message\"]',\n            'button[title=\"Send message\"]',\n        ]));\n        if (!sendRes?.ok) {\n            // Fallback: try clickByText.\n            const textRes = await evaluateQoder(page, clickByTextScript(['Send message', 'Send', '发送']));\n            if (!textRes?.ok) throw new CommandExecutionError('Send button not found', '');\n        }\n        const afterCount = await waitForMessageCountGrowth(page, beforeCount);\n        if (Number(afterCount) <= Number(beforeCount)) {\n            throw new CommandExecutionError('Qoder send did not create a new visible message row');\n        }\n        return [{ Status: 'sent', Length: String(text.length) }];\n    },\n});\n\n// -------- ask --------\ncli({\n    site: 'qoder',\n    name: 'ask',\n    access: 'write',\n    description: 'Send a prompt to Qoder and wait up to --timeout seconds for the reply (best-effort: polls for the chat turn count to grow + stabilize).',\n    domain: 'localhost',\n    strategy: Strategy.UI,\n    browser: true,","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/qoder/quest.js#L60-L96","documentation":"A CommandExecutionError thrown by the qoder send command after both send strategies fail: first clicking button[aria-label=\"Send message\"] / button[title=\"Send message\"] via clickFirstScript, then the clickByTextScript fallback with 'Send message' / 'Send' / '发送'. If neither finds a clickable send control, this error is thrown — the text may have been typed but never sent.","triggerScenarios":"Running `qoder send <text>` when: the composer is empty in Qoder's eyes (injection silently failed or text was rejected), so the Send button is disabled/hidden; Qoder changed the aria-label/title (e.g. to 'Send' or an icon with different metadata); the send control is now an icon-only SVG button with no accessible label or text; or the UI is mid-render.","commonSituations":"Qoder redesigns replacing the labeled button with an icon-only button lacking aria-label; localized builds where text candidates ('Send message'/'Send'/'发送') don't match; Enter-to-send-only configurations with no visible button; automation racing the composer's enabled state after typing.","solutions":["Retry with a short wait after typing — the Send button only becomes enabled once the composer registers the injected text.","Inspect the composer DOM and add the current selector (new aria-label or a form submit selector) to the clickFirstScript selector list.","Add a keyboard fallback: dispatch an Enter keypress on the composer, which Qoder treats as send.","Verify text injection succeeded first (composer value non-empty) since a disabled Send is usually a symptom of failed injection, not a missing button."],"exampleFix":"// before\nif (!textRes?.ok) throw new CommandExecutionError('Send button not found', '');\n// after\nif (!textRes?.ok) {\n    const enterRes = await evaluateQoder(page, pressEnterInComposerScript());\n    if (!enterRes?.ok) throw new CommandExecutionError('Send button not found', '');\n}","handlingStrategy":"fallback","validationCode":"// Verify injection registered and a send control exists before invoking\nconst state = await page.evaluate(() => {\n  const el = document.querySelector('textarea, [contenteditable=\"true\"]');\n  const hasText = !!el && (el.value || el.innerText || '').trim().length > 0;\n  const hasSend = !!document.querySelector('button[aria-label=\"Send message\"], button[title=\"Send message\"]');\n  return { hasText, hasSend };\n});\nif (!state.hasText || !state.hasSend) throw new Error('Composer empty or Send control missing — send would fail');","typeGuard":"function hasClickResult(res) {\n  return typeof res === 'object' && res !== null && res.ok === true;\n}","tryCatchPattern":"try {\n  await runCli('qoder send', [text]);\n} catch (e) {\n  if (String(e.message).includes('Send button not found')) {\n    // keyboard fallback: focus composer and press Enter\n    await focusComposerAndPressEnter();\n  } else throw e;\n}","preventionTips":["Confirm the composer holds text (Send is disabled when empty) before expecting a click.","Re-check aria-labels/title attributes after every Qoder update.","Maintain an Enter-key fallback for icon-only or relabeled send buttons.","Allow a short post-typing delay so the button transitions to enabled."],"tags":["ui-automation","dom-click","browser","qoder"],"backgroundTag":"ui-element-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}