jackwener/OpenCLI · error · CommandExecutionError

Failed to send message

Error message

Failed to send message

What it means

This CommandExecutionError is thrown by the `claude send` command when sendMessage reports failure — the error message is sendResult.reason if provided, otherwise the generic 'Failed to send message'. It means the prompt could not be injected/submitted into the composer and sent, despite the composer being present.

Source

Thrown at clis/claude/send.js:40

        const prompt = requireNonEmptyPrompt(kwargs.prompt, 'claude send');

        if (parseBoolFlag(kwargs.new)) {
            await page.goto(CLAUDE_URL);
            try {
                await page.wait({ selector: COMPOSER_SELECTOR, timeout: 8 });
            } catch {
                // Composer didn't mount; ensureClaudeComposer below surfaces a typed error.
            }
        } else {
            // ensureOnClaude now waits for the composer selector; the previous
            // post-nav 2 s settle is covered by that event-based wait.
            await ensureOnClaude(page);
        }
        await withRetry(() => ensureClaudeComposer(page, 'Claude send requires a visible composer on the current page.'));

        const sendResult = await withRetry(() => sendMessage(page, prompt));
        if (!sendResult?.ok) {
            throw new CommandExecutionError(sendResult?.reason || 'Failed to send message');
        }
        return [{
            Status: 'Success',
            SubmittedBy: sendResult.method || 'send-button',
            InjectedText: prompt,
        }];
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read sendResult.reason in the message (when present) for the specific cause and address it directly
  2. Re-run the command — transient composer/submit races are often resolved on retry, and withRetry already retried the immediate window
  3. Shorten the prompt if it may exceed Claude's message limits, and verify the conversation isn't full/at capacity
  4. Refresh the conversation page in the automated browser (or rerun the command to reopen) so a stale DOM is replaced
  5. Update the library if Claude changed its composer/send-button markup

Example fix

// before
opencli claude send "<very long prompt>"   // send button disabled -> Failed to send message
// after
opencli claude send "shorter prompt"        # retry with valid-length prompt after page settles
Defensive patterns

Strategy: retry

Validate before calling

// preflight: composer must exist and the prompt must be non-empty/reasonable
if (!prompt || prompt.length > 100000) throw new Error('Prompt missing or too long for claude.ai');
const state = await getPageState(page);
if (!state.isLoggedIn) throw new Error('Run `opencli claude auth` first');

Type guard

function isSendSuccess(sendResult) {
  return !!sendResult && sendResult.ok === true && typeof sendResult.method === 'string';
}

Try / catch

try {
  await opencli.claude.send(prompt);
} catch (e) {
  if (e.message === 'Failed to send message' || e.message.startsWith('send failed')) {
    await sleep(2000);
    return opencli.claude.send(prompt);      // one retry after page settles
  }
  throw e;
}

Prevention

When it happens

Trigger: withRetry(() => sendMessage(page, prompt)) returns { ok: false } — the send button was not clickable, text injection failed, the composer was disabled (e.g. Claude at capacity or message queue full), the page navigated mid-send, or a send-button/textarea DOM change broke the send routine.

Common situations: Claude conversation is full or the model is overloaded so the send button is disabled; message length exceeds limits; Claude DOM update moving the send button; page reloaded between composer check and send; network hiccup during submission; rate limiting by Claude.

Related errors


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