jackwener/OpenCLI · error · CommandExecutionError

Gemini conversation list returned a malformed row

Error message

Gemini conversation list returned a malformed row

What it means

Thrown by CommandExecutionError when the Gemini conversation-list extraction script returns a row that is not an object with string title and url fields. The library validates every row returned by getGeminiConversationListScript inside page.evaluate before mapping it to { Title, Url }, because the sidebar DOM can yield anchors that don't carry the expected data.

Source

Thrown at clis/gemini/utils.js:1151

export async function startNewGeminiChat(page) {
    await ensureGeminiPage(page);
    const action = await page.evaluate(clickNewChatScript());
    if (action === 'navigate') {
        await page.goto(GEMINI_APP_URL, { waitUntil: 'load', settleMs: 2500 });
    }
    await page.wait(1);
    return action;
}
export async function getGeminiConversationList(page) {
    await ensureGeminiPage(page);
    let rows = [];
    // Gemini collapses the sidebar "最近"/Recents section by default, hiding
    // the conversation anchors. Retry extraction after expanding it.
    for (let attempt = 0; attempt < 3; attempt += 1) {
        const raw = requireGeminiArrayResult(await page.evaluate(getGeminiConversationListScript()), 'Gemini conversation list');
        rows = raw.flatMap((item) => {
            if (!isObjectRecord(item) || typeof item.title !== 'string' || typeof item.url !== 'string') {
                throw new CommandExecutionError('Gemini conversation list returned a malformed row');
            }
            if (!isGeminiConversationUrl(item.url))
                return [];
            return { Title: item.title, Url: item.url };
        });
        if (rows.length > 0)
            break;
        const changedRaw = unwrapGeminiEvaluateResult(await page.evaluate(expandGeminiRecentScript()), 'Gemini sidebar expand');
        const changed = changedRaw === true;
        // Give the React sidebar a moment to render the expanded anchors,
        // longer on the first expansion (sidebar slide-in is async).
        await page.wait(attempt === 0 ? 1.2 : 0.6);
        if (!changed && attempt > 0)
            break;
    }
    return rows;
}
export async function clickGeminiConversationByTitle(page, query) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Wait for the sidebar Recents section to finish loading (the code already retries 3x after expanding it — check whether retries were exhausted).
  2. Inspect one raw row returned by getGeminiConversationListScript and update the extraction script to the current sidebar DOM shape.
  3. Filter out placeholder/empty rows inside the in-page script before returning them.
  4. Wrap the call in try/catch for CommandExecutionError and degrade gracefully to an empty list.
Defensive patterns

Strategy: try-catch

Type guard

function isConversationRow(row) {
  return typeof row === 'object' && row !== null &&
    typeof row.title === 'string' && typeof row.url === 'string';
}

Try / catch

let conversations = [];
try {
  conversations = await listGeminiConversations(page);
} catch (e) {
  if (String(e.message).includes('malformed row')) conversations = []; // degrade gracefully
  else throw e;
}

Prevention

When it happens

Trigger: Calling the conversation-list command when page.evaluate(getGeminiConversationListScript()) returns array entries missing title or url, or where those fields are non-strings (e.g. null entries from empty/placeholder sidebar items, sponsored rows, or a changed DOM shape).

Common situations: Gemini updated the Recents sidebar markup so the extraction script returns raw nodes instead of { title, url } objects; locale-specific sidebar entries with empty titles; mocked/partial test data; scraping while the sidebar is still loading.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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