{"record":{"id":"a9bea927524f92a7","repo":"jackwener/OpenCLI","slug":"gemini-page-snapshot-returned-a-malformed-result","errorCode":null,"errorMessage":"Gemini page snapshot returned a malformed result","messagePattern":"Gemini page snapshot returned a malformed result","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/gemini/utils.js","lineNumber":1234,"sourceCode":"export async function getLatestGeminiAssistantResponse(page) {\n    await ensureGeminiPage(page);\n    const turns = await getGeminiVisibleTurns(page);\n    const assistantTurn = [...turns].reverse().find((turn) => turn.Role === 'Assistant');\n    if (assistantTurn?.Text) {\n        return sanitizeGeminiResponseText(assistantTurn.Text, '');\n    }\n    const lines = await getGeminiTranscriptLines(page);\n    return lines.join('\\n').trim();\n}\nexport async function readGeminiSnapshot(page) {\n    await ensureGeminiPage(page);\n    const snapshot = requireGeminiObjectResult(await page.evaluate(readGeminiSnapshotScript()), 'Gemini page snapshot');\n    if (!Array.isArray(snapshot.turns) ||\n        !Array.isArray(snapshot.transcriptLines) ||\n        typeof snapshot.composerHasText !== 'boolean' ||\n        typeof snapshot.isGenerating !== 'boolean' ||\n        typeof snapshot.structuredTurnsTrusted !== 'boolean') {\n        throw new CommandExecutionError('Gemini page snapshot returned a malformed result');\n    }\n    return snapshot;\n}\nfunction findLastUserTurnIndex(turns) {\n    for (let index = turns.length - 1; index >= 0; index -= 1) {\n        if (turns[index]?.Role === 'User')\n            return index;\n    }\n    return null;\n}\nfunction findLastUserTurn(turns) {\n    const index = findLastUserTurnIndex(turns);\n    return index === null ? null : turns[index] ?? null;\n}\nexport async function waitForGeminiSubmission(page, before, timeoutSeconds) {\n    const preSendAssistantCount = before.turns.filter((turn) => turn.Role === 'Assistant').length;\n    const maxPolls = Math.max(1, Math.ceil(timeoutSeconds));\n    for (let index = 0; index < maxPolls; index += 1) {","sourceCodeStart":1216,"sourceCodeEnd":1252,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/gemini/utils.js#L1216-L1252","documentation":"Thrown by readGeminiSnapshot when the snapshot object returned by readGeminiSnapshotScript lacks the required shape: turns and transcriptLines arrays, and boolean composerHasText, isGenerating, and structuredTurnsTrusted fields. The library uses this snapshot for submission detection, so a malformed result would silently break polling logic; it fails fast instead.","triggerScenarios":"Any snapshot read (used by waitForGeminiSubmission, isGeminiGenerating flows, sendGeminiMessage) where page.evaluate returns an object missing/incorrectly-typed fields — e.g. the in-page script threw partially and returned an error stub, or the script version in the page predates a new field like structuredTurnsTrusted.","commonSituations":"Mixing library versions or cached page scripts that return older snapshot shapes; evaluate result unwrapping producing undefined fields; Gemini page in an error state where the snapshot script bails early; tests with hand-written snapshot mocks missing new fields.","solutions":["Update any mocked snapshot fixtures to include all five required fields with correct types.","Re-load the page (fresh goto/ensureGeminiPage) so the current snapshot script runs completely.","Check that page.evaluate isn't returning an error/undefined object — log the raw result before validation.","Catch CommandExecutionError and retry the snapshot read with backoff."],"exampleFix":"// before (test mock)\n{ turns: [], transcriptLines: [], composerHasText: false, isGenerating: false }\n// after\n{ turns: [], transcriptLines: [], composerHasText: false, isGenerating: false, structuredTurnsTrusted: true }","handlingStrategy":"validation","validationCode":"// validate a hand-built snapshot before passing it into helpers\nfunction isValidSnapshot(s) {\n  return !!s && Array.isArray(s.turns) && Array.isArray(s.transcriptLines) &&\n    typeof s.composerHasText === 'boolean' && typeof s.isGenerating === 'boolean' &&\n    typeof s.structuredTurnsTrusted === 'boolean';\n}","typeGuard":"function isGeminiSnapshot(s) {\n  return typeof s === 'object' && s !== null && Array.isArray(s.turns) &&\n    Array.isArray(s.transcriptLines) && typeof s.composerHasText === 'boolean' &&\n    typeof s.isGenerating === 'boolean' && typeof s.structuredTurnsTrusted === 'boolean';\n}","tryCatchPattern":"let snapshot;\ntry {\n  snapshot = await readGeminiSnapshot(page);\n} catch (e) {\n  if (String(e.message).includes('malformed result')) { await page.wait(1); snapshot = await readGeminiSnapshot(page); }\n  else throw e;\n}","preventionTips":["Keep test mocks in sync with the current snapshot schema (all five fields).","Reload the page if snapshot reads start failing after a long session.","Log raw evaluate results when debugging to see the actual shape returned.","Retry snapshot reads — they are cheap and idempotent."],"tags":["gemini","schema-validation","page-evaluate","snapshot"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}