jackwener/OpenCLI · error · CommandExecutionError

composer type failed

Error message

composer type failed

What it means

A CommandExecutionError thrown by the qoder send command when the text-injection script (buildQoderInjectTextScript) fails to type the prompt into the composer. The message prefers the script's own reason (typeRes?.reason) and falls back to this literal string. It means DOM evaluation to fill the composer textarea returned ok:false — the send step is never reached.

Source

Thrown at clis/qoder/quest.js:67

cli({
    site: 'qoder',
    name: 'send',
    access: 'write',
    description: 'Type text into the Qoder composer and click "Send message" (fire-and-forget).',
    domain: 'localhost',
    strategy: Strategy.UI,
    browser: true,
    args: [
        { name: 'text', positional: true, required: true, help: 'Text to send' },
    ],
    columns: ['Status', 'Length'],
    func: async (page, kwargs) => {
        const text = String(kwargs?.text || '').trim();
        if (!text) throw new ArgumentError('text is required');

        const beforeCount = await evaluateQoder(page, QODER_MESSAGE_COUNT_JS);
        const typeRes = await evaluateQoder(page, buildQoderInjectTextScript(text));
        if (!typeRes?.ok) throw new CommandExecutionError(typeRes?.reason || 'composer type failed', '');
        await page.wait(0.3);

        // Click Send message
        const sendRes = await evaluateQoder(page, clickFirstScript([
            'button[aria-label="Send message"]',
            'button[title="Send message"]',
        ]));
        if (!sendRes?.ok) {
            // Fallback: try clickByText.
            const textRes = await evaluateQoder(page, clickByTextScript(['Send message', 'Send', '发送']));
            if (!textRes?.ok) throw new CommandExecutionError('Send button not found', '');
        }
        const afterCount = await waitForMessageCountGrowth(page, beforeCount);
        if (Number(afterCount) <= Number(beforeCount)) {
            throw new CommandExecutionError('Qoder send did not create a new visible message row');
        }
        return [{ Status: 'sent', Length: String(text.length) }];
    },

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Confirm the Qoder chat view is open and the composer is visible before running send; retry after a short wait.
  2. If the reason indicates a selector miss, inspect the composer DOM and update buildQoderInjectTextScript's selector to match the current markup.
  3. If the value set is reverted (React controlled input), switch the injection to input-event-based methods: document.execCommand('insertText') or native setter + dispatched InputEvent.
  4. Re-attach to the correct webview/window (close extra Qoder windows) and rerun.

Example fix

// before
const typeRes = await evaluateQoder(page, buildQoderInjectTextScript(text));
if (!typeRes?.ok) throw new CommandExecutionError(typeRes?.reason || 'composer type failed', '');
// after
await page.wait(1); // ensure composer mounted
let typeRes = await evaluateQoder(page, buildQoderInjectTextScript(text));
if (!typeRes?.ok) typeRes = await evaluateQoder(page, insertTextViaExecCommandScript(text)); // contenteditable fallback
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the composer exists and is editable before invoking send
const composerReady = await page.evaluate(() => {
  const el = document.querySelector('textarea, [contenteditable="true"]');
  return !!el && !el.disabled && !el.readOnly;
});
if (!composerReady) throw new Error('Qoder composer not ready — wait for the chat view to mount');

Type guard

function injectionSucceeded(res) {
  return typeof res === 'object' && res !== null && res.ok === true;
}

Try / catch

try {
  await runCli('qoder send', [text]);
} catch (e) {
  if (String(e.message).includes('composer type failed')) {
    await sleep(1000);
    await runCli('qoder send', [text]); // retry after composer mounts
  } else throw e;
}

Prevention

When it happens

Trigger: Running `qoder send <text>` when: the composer textarea/input does not exist yet (UI still loading or wrong panel active); the injection script targeted a selector Qoder changed in an update; the webview is in a state where execCommand/insertText or setting .value + dispatching input events is blocked (React controlled components resetting value); or evaluateQoder could not attach to the page.

Common situations: Qoder updates switching the composer from a plain textarea to a contenteditable/ProseMirror editor, breaking value-based injection; sending before the chat view finishes mounting; multiple Qoder windows causing evaluateQoder to target a stale frame.

Related errors


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