jackwener/OpenCLI · error · CommandExecutionError

No assistant reply found in Qianwen chat.

Error message

No assistant reply found in Qianwen chat.

What it means

CommandExecutionError thrown when waitForAnswer finished without a timeout or auth failure but returned no assistant object — the reply could not be located/parsed in the chat DOM.

Source

Thrown at clis/qwen/ask.js:79

        // Anchor on the visible transcript BEFORE sending so waitForAnswer can
        // bind the reply to the newly sent prompt instead of an older answer.
        const baselineAnchor = await getBaselineChatAnchor(page);

        const send = await sendMessage(page, prompt);
        if (!send?.ok) {
            if (await hasLoginGate(page)) throw authRequired();
            throw new CommandExecutionError(send?.reason || 'Failed to send Qianwen prompt');
        }

        const result = await waitForAnswer(page, prompt, timeout, baselineAnchor);
        if (result.status === 'auth_required') throw authRequired();
        if (result.status === 'timeout') {
            throw new TimeoutError('qianwen ask', timeout, 'No Qianwen reply observed before timeout. Retry with --timeout increased.');
        }
        const assistant = result.assistant;
        if (!assistant) {
            throw new CommandExecutionError('No assistant reply found in Qianwen chat.');
        }
        const answer = wantMarkdown && assistant.html
            ? (bubbleHtmlToMarkdown(assistant.html) || assistant.text)
            : assistant.text;
        return [
            { Role: 'User', Text: prompt },
            { Role: 'Assistant', Text: answer },
        ];
    },
});

async function getBaselineChatAnchor(page) {
    const bubbles = await getMessageBubbles(page);
    const lastBubbleId = bubbles.length ? bubbles[bubbles.length - 1].id : '';
    let lastAssistantId = '';
    for (let i = bubbles.length - 1; i >= 0; i -= 1) {
        if (bubbles[i].role === 'Assistant') {
            lastAssistantId = bubbles[i].id;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Inspect the page DOM after failure and update the assistant-bubble selector used by waitForAnswer.
  2. Retry without --markdown to rule out HTML->markdown conversion producing empty output.
  3. Check whether Qianwen returned a refusal/error card and adjust prompting.
  4. Upgrade the library if a newer release adapts to the new UI.

Example fix

// before
const ans = await qwenAsk(page, { prompt: q, markdown: true });
// after
let ans;
try { ans = await qwenAsk(page, { prompt: q, markdown: true }); }
catch (e) {
  if (String(e.message).includes('No assistant reply')) {
    ans = await qwenAsk(page, { prompt: q, markdown: false });
  } else throw e;
}
Defensive patterns

Strategy: fallback

Validate before calling

// no pre-call check possible; mitigate by disabling markdown conversion path
const opts = { prompt, markdown: false };

Type guard

function isNoReplyError(e) {
  return e instanceof CommandExecutionError && e.message.includes('No assistant reply');
}

Try / catch

try {
  ans = await qwenAsk(page, { prompt, markdown: true });
} catch (e) {
  if (isNoReplyError(e)) {
    ans = await qwenAsk(page, { prompt, markdown: false });
  } else { throw e; }
}

Prevention

When it happens

Trigger: waitForAnswer returns with result.assistant undefined: the reply bubble selector mismatched, the chat DOM structure changed, the response was an error card instead of an answer, or bubbleHtmlToMarkdown produced no usable text path.

Common situations: Qianwen frontend update renaming reply container classes; answer rendered as an error/filtered-content notice; streaming UI state the extractor does not handle; wantMarkdown conversion failing silently leaving empty text.

Related errors


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