{"record":{"id":"11d62f27479f3d17","repo":"jackwener/OpenCLI","slug":"mercury-appears-to-have-an-existing-expense-review","errorCode":null,"errorMessage":"Mercury appears to have an existing expense review open; refusing to click a possible final Submit expense button","messagePattern":"Mercury appears to have an existing expense review open; refusing to click a possible final Submit expense button","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"warning","filePath":"clis/mercury/reimbursement-draft.js","lineNumber":49,"sourceCode":"        { name: 'currency', default: 'CNY', help: 'Original currency code' },\n        { name: 'date', required: true, help: 'Expense date as YYYY-MM-DD' },\n        { name: 'merchant', required: true, help: 'Merchant shown on the reimbursement' },\n        { name: 'category', default: 'Marketing & Advertising', help: 'Mercury expense category' },\n        { name: 'notes', required: true, help: 'Business purpose / reimbursement notes' },\n        { name: 'ocr-wait-seconds', default: '8', help: 'Seconds to wait after receipt upload before correcting OCR-overwritten fields' },\n        { name: 'close-after-review', type: 'boolean', default: false, help: 'Close the Review dialog after verification; final Submit is still never clicked' },\n    ],\n    columns: ['status', 'url', 'receipt', 'uploaded', 'fieldsTouched', 'reviewReady', 'submitBlocked', 'warnings'],\n    func: async (page, kwargs) => {\n        const input = normalizeReimbursementInput(kwargs);\n        const before = await inspectMercury(page);\n        assertLoggedIn(before);\n\n        await page.goto(MERCURY_EXPENSES_URL, { waitUntil: 'load', settleMs: 1500 });\n        await page.wait({ time: 1 });\n        const createSurface = await assertCreateExpenseSurface(page);\n        if (createSurface.hasFinalSubmit && /review/i.test(createSurface.bodyPreview || '')) {\n            throw new CommandExecutionError('Mercury appears to have an existing expense review open; refusing to click a possible final Submit expense button');\n        }\n\n        const opened = await clickCreateExpenseButton(page);\n        if (!opened.clicked) {\n            throw new CommandExecutionError('Could not find the Mercury Submit expense or New expense button');\n        }\n\n        await page.wait({ time: 2 });\n\n        if (!page.uploadFiles) {\n            throw new CommandExecutionError('Mercury reimbursement-draft requires Browser Bridge uploadFiles support to verify the intended receipt input');\n        }\n        const upload = await page.uploadFiles(RECEIPT_INPUT_SELECTOR, [input.receipt]);\n        if (!upload || upload.uploaded !== true || upload.files !== 1) {\n            throw new CommandExecutionError('Mercury receipt upload did not confirm exactly one uploaded file');\n        }\n        if (upload.target !== RECEIPT_INPUT_SELECTOR || upload.matches_n !== 1) {\n            throw new CommandExecutionError('Mercury receipt upload did not confirm the intended receipt input');","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/mercury/reimbursement-draft.js#L31-L67","documentation":"Before creating a Mercury reimbursement draft, the flow inspects the expenses page via assertCreateExpenseSurface. If the page already shows a final 'Submit expense' control within a review view (hasFinalSubmit plus a /review/i body preview), the automation refuses to click, since clicking could submit a pre-existing unfinished expense. It throws CommandExecutionError with this safety message.","triggerScenarios":"Running the reimbursement-draft command while Mercury already has an expense review screen open — e.g. a previous draft left mid-flow, a prior automation run crashed after opening the review, or a human left the review page open in the shared browser session.","commonSituations":"Retrying the command after a failed earlier run that left state behind; multiple users/automations sharing one Mercury browser profile; resuming work where the last session ended on the review step.","solutions":["Open the Mercury expenses page manually and complete or discard the open expense review, then rerun the command.","Cancel/close the in-progress expense in the Mercury UI so the create surface is clean.","Restart the browser session with a fresh page (about:blank or re-login) so no review state is carried over.","If it recurs, check whether a prior automation run is leaking sessions and ensure it closes or resets the page on exit."],"exampleFix":"// before (session reused with stale review open)\nawait draftReimbursement(page, {...}); // refuses: existing review open\n// after\nawait page.goto('about:blank');\nawait draftReimbursement(page, {...}); // fresh create surface","handlingStrategy":"try-catch","validationCode":"// preflight: ensure no review surface is open before automating\nconst body = await page.evaluate('document.body.innerText');\nif (/review/i.test(body) && /submit expense/i.test(body)) {\n  throw new Error('Complete or discard the open Mercury expense review first');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await draftReimbursement(page, data);\n} catch (e) {\n  if (/existing expense review open/.test(e.message)) {\n    console.error('Resolve the open review in Mercury UI (or reset the page), then rerun');\n  } else throw e;\n}","preventionTips":["Reset the browser page (about:blank or fresh session) before each automation run.","Manually complete/discard leftover drafts after failed runs.","Dedicate one browser profile per automation to avoid human/automation conflicts.","Make cleanup part of your automation's finally block so sessions never end mid-review."],"tags":["browser-automation","safety-guard","mercury","command-execution-error"],"backgroundTag":"stale-automation-state","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}