jackwener/OpenCLI · error · CommandExecutionError
Mercury is already showing a submit/review surface; refusing
Error message
Mercury is already showing a submit/review surface; refusing to click a possible final Submit expense button
What it means
This is an intentional safety stop in clickCreateExpenseButton(). The injected script detects when the only 'Submit expense'-matching button lives inside a dialog/form/review context — i.e. Mercury is already showing a final Submit expense (review) surface. Clicking again could resubmit or create a duplicate expense, so the library refuses and throws instead of clicking.
Source
Thrown at clis/mercury/utils.js:197
const dangerous = candidates.find((node) => {
const container = node.closest('[role="dialog"], dialog, form, [aria-modal="true"]');
const context = String(container?.innerText || container?.textContent || '').replace(/\\s+/g, ' ').trim();
return Boolean(container) || /Review|receipt|amount|merchant|category|notes|expense date/i.test(context);
});
if (dangerous) {
return { clicked: false, blocked: true, text: dangerous.innerText || dangerous.textContent || '' };
}
const el = candidates[0];
if (!el) return { clicked: false, blocked: false };
el.click();
return { clicked: true, blocked: false, text: el.innerText || el.textContent || '' };
})()`);
const payload = assertObject(result, 'create expense click result');
if (typeof payload.clicked !== 'boolean' || typeof payload.blocked !== 'boolean') {
throw new CommandExecutionError('Mercury returned malformed create expense click result');
}
if (payload.blocked) {
throw new CommandExecutionError('Mercury is already showing a submit/review surface; refusing to click a possible final Submit expense button');
}
return payload;
}
export async function assertCreateExpenseSurface(page) {
const state = await page.evaluate(`(() => {
const text = document.body?.innerText || '';
return {
url: location.href,
title: document.title,
hasFinalSubmit: /Submit expense/i.test(text) && /Review|receipt|amount|merchant|category|notes/i.test(text),
hasForm: /receipt|amount|merchant|category|notes|expense date/i.test(text),
bodyPreview: text.replace(/\\s+/g, ' ').trim().slice(0, 600)
};
})()`);
const payload = assertBooleanFields(assertMercuryState(state, 'expense form state'), 'expense form state', ['hasFinalSubmit', 'hasForm']);
if (payload.hasFinalSubmit && !payload.hasForm) {
throw new CommandExecutionError('Mercury is showing a submit button without a recognizable expense form; refusing to click');View on GitHub (pinned to 49907e53dc)
Solutions
- Do not retry the click; treat the expense as already at the review stage and assert the surface with assertCreateExpenseSurface().
- Navigate back to the expenses list (inspectMercury / MERCURY_EXPENSES_URL) before attempting to open the create-expense form again.
- Guard the caller (`opened`) so clickCreateExpenseButton only runs once per session, e.g. a `createSurface` state flag.
- If a duplicate flow is needed, start a fresh page/session.
Example fix
// before
await clickCreateExpenseButton(page); // may throw if review surface already open
// after
const state = await assertCreateExpenseSurface(page);
if (!state.hasFinalSubmit) {
await clickCreateExpenseButton(page);
} Defensive patterns
Strategy: validation
Validate before calling
const state = await assertCreateExpenseSurface(page);
if (!state.hasFinalSubmit) {
await clickCreateExpenseButton(page);
} Try / catch
try {
await clickCreateExpenseButton(page);
} catch (err) {
if (err instanceof CommandExecutionError && /refusing to click a possible final Submit/.test(err.message)) {
// expense surface already open — skip instead of retrying
return { skipped: true };
}
throw err;
} Prevention
- Track flow state (a `createSurface` flag) so the button is clicked at most once per session.
- Never blindly retry after this error — the guard exists to prevent duplicate expenses.
- Re-run inspectMercury/assertCreateExpenseSurface after any navigation before clicking.
- Treat the error as informational: the expense form is already open or submitted.
When it happens
Trigger: clickCreateExpenseButton() is called while the page already shows a submit/review dialog: the injected script's `dangerous` check finds a matching button inside [role="dialog"], dialog, form, or [aria-modal="true"], or the container text matches /Review|receipt|amount|merchant|category|notes|expense date/i, setting blocked=true.
Common situations: Calling the create-expense flow twice in one session after the expense form was already submitted; retry logic re-invoking clickCreateExpenseButton after a timeout while the review screen is open; resuming an automation run mid-flow.
Related errors
- Mercury is showing a submit button without a recognizable ex
- Mercury appears to have an existing expense review open; ref
- Could not find the Mercury Submit expense or New expense but
- Mercury reimbursement-draft requires Browser Bridge uploadFi
- Mercury receipt upload did not confirm exactly one uploaded
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/365064ca066ae7e3.
Report an issue: GitHub.