{"record":{"id":"322b538ca7d1fe23","repo":"jackwener/OpenCLI","slug":"mercury-returned-malformed-create-expense-click-re","errorCode":null,"errorMessage":"Mercury returned malformed create expense click result","messagePattern":"Mercury returned malformed create expense click result","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/mercury/utils.js","lineNumber":194,"sourceCode":"            return style.visibility !== 'hidden' && style.display !== 'none' && node.offsetParent !== null;\n          })\n          .filter((node) => wanted.has(norm(node.innerText || node.textContent || '')));\n        const dangerous = candidates.find((node) => {\n          const container = node.closest('[role=\"dialog\"], dialog, form, [aria-modal=\"true\"]');\n          const context = String(container?.innerText || container?.textContent || '').replace(/\\\\s+/g, ' ').trim();\n          return Boolean(container) || /Review|receipt|amount|merchant|category|notes|expense date/i.test(context);\n        });\n        if (dangerous) {\n          return { clicked: false, blocked: true, text: dangerous.innerText || dangerous.textContent || '' };\n        }\n        const el = candidates[0];\n        if (!el) return { clicked: false, blocked: false };\n        el.click();\n        return { clicked: true, blocked: false, text: el.innerText || el.textContent || '' };\n    })()`);\n    const payload = assertObject(result, 'create expense click result');\n    if (typeof payload.clicked !== 'boolean' || typeof payload.blocked !== 'boolean') {\n        throw new CommandExecutionError('Mercury returned malformed create expense click result');\n    }\n    if (payload.blocked) {\n        throw new CommandExecutionError('Mercury is already showing a submit/review surface; refusing to click a possible final Submit expense button');\n    }\n    return payload;\n}\n\nexport async function assertCreateExpenseSurface(page) {\n    const state = await page.evaluate(`(() => {\n        const text = document.body?.innerText || '';\n        return {\n            url: location.href,\n            title: document.title,\n            hasFinalSubmit: /Submit expense/i.test(text) && /Review|receipt|amount|merchant|category|notes/i.test(text),\n            hasForm: /receipt|amount|merchant|category|notes|expense date/i.test(text),\n            bodyPreview: text.replace(/\\\\s+/g, ' ').trim().slice(0, 600)\n        };\n    })()`);","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/mercury/utils.js#L176-L212","documentation":"clickCreateExpenseButton() injects a script that looks for a 'Submit expense'/'New expense' button and returns { clicked, blocked }. It throws this error when the returned payload is an object but `clicked` or `blocked` is not a boolean, meaning the browser-side script did not produce the expected contract. This is a defensive shape check before any further action is taken.","triggerScenarios":"The page.evaluate() injected in clickCreateExpenseButton returns an object missing or mistyping `clicked`/`blocked` — corrupted return value, mid-navigation, or a modified/older injected script.","commonSituations":"Mercury UI updates that break the script's early-return paths; evaluate called on a navigating/crashed page so serialization returns an unexpected value; test harnesses stubbing page.evaluate with wrong shapes.","solutions":["Inspect the raw evaluate result and confirm `clicked` and `blocked` are present and boolean.","Reload the Mercury page and retry via the normal `opened` flow.","Align page.evaluate stubs/mocks in tests with the { clicked: boolean, blocked: boolean } contract.","Upgrade the mercury utils to the version matching the current UI."],"exampleFix":"// before\nif (typeof payload.clicked !== 'boolean' || typeof payload.blocked !== 'boolean') {\n  throw new CommandExecutionError('Mercury returned malformed create expense click result');\n}\n// after\nif (typeof payload.clicked !== 'boolean' || typeof payload.blocked !== 'boolean') {\n  throw new CommandExecutionError(`Malformed create expense click result: ${JSON.stringify(payload)}`);\n}","handlingStrategy":"type-guard","validationCode":"const payload = await page.evaluate(`(() => { ... })()`);\nif (payload && typeof payload.clicked === 'boolean' && typeof payload.blocked === 'boolean') {\n  await clickCreateExpenseButton(page);\n}","typeGuard":"function isCreateExpenseClickResult(v) {\n  return v != null && typeof v === 'object'\n    && typeof v.clicked === 'boolean' && typeof v.blocked === 'boolean';\n}","tryCatchPattern":"try {\n  const res = await clickCreateExpenseButton(page);\n} catch (err) {\n  if (err instanceof CommandExecutionError && /malformed create expense click result/.test(err.message)) {\n    // reload page and retry once; else surface the error\n  } else throw err;\n}","preventionTips":["Never stub page.evaluate in tests with shapes missing `blocked`.","Run the create-expense flow only once per session to avoid mid-navigation evaluates.","Reload the page before retrying after a malformed result.","Pin the mercury utils version to one tested against the current UI."],"tags":["browser-automation","page-evaluate","schema-validation-failed","mercury"],"backgroundTag":"page-evaluate-result-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}