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.

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.

Appendix: 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)