jackwener/OpenCLI · error · CommandExecutionError
Mercury receipt upload did not confirm the expected receipt
Error message
Mercury receipt upload did not confirm the expected receipt file
What it means
Thrown when the bridge's upload result `file_names` array does not contain the basename of the supplied receipt path, meaning the file that was actually attached is not the receipt the user passed via --receipt. This guards against the wrong file (or a placeholder/stale file) being attached to the Mercury expense.
Source
Thrown at clis/mercury/reimbursement-draft.js:71
if (!opened.clicked) {
throw new CommandExecutionError('Could not find the Mercury Submit expense or New expense button');
}
await page.wait({ time: 2 });
if (!page.uploadFiles) {
throw new CommandExecutionError('Mercury reimbursement-draft requires Browser Bridge uploadFiles support to verify the intended receipt input');
}
const upload = await page.uploadFiles(RECEIPT_INPUT_SELECTOR, [input.receipt]);
if (!upload || upload.uploaded !== true || upload.files !== 1) {
throw new CommandExecutionError('Mercury receipt upload did not confirm exactly one uploaded file');
}
if (upload.target !== RECEIPT_INPUT_SELECTOR || upload.matches_n !== 1) {
throw new CommandExecutionError('Mercury receipt upload did not confirm the intended receipt input');
}
const uploadedNames = Array.isArray(upload.file_names) ? upload.file_names : [];
if (!uploadedNames.includes(receiptBasename(input.receipt))) {
throw new CommandExecutionError('Mercury receipt upload did not confirm the expected receipt file');
}
if (input.ocrWaitSeconds > 0) await page.wait({ time: input.ocrWaitSeconds });
const fields = await fillReimbursementFields(page, input);
await page.wait({ time: 1 });
const expectedFields = ['amount', 'currency', 'date', 'merchant', 'category', 'notes'];
const missing = expectedFields.filter((key) => !fields.touched[key]);
if (missing.length > 0) {
throw new CommandExecutionError(`Mercury reimbursement form fields were not all filled: ${missing.join(', ')}`);
}
const reviewClick = await page.evaluate(`(() => {
const norm = (s) => String(s || '').replace(/\\s+/g, ' ').trim().toLowerCase();
const candidates = Array.from(document.querySelectorAll('button, a, [role="button"], [role="link"]'))
.filter((node) => {
const style = window.getComputedStyle(node);
return style.visibility !== 'hidden' && style.display !== 'none' && node.offsetParent !== null;View on GitHub (pinned to 49907e53dc)
Solutions
- Pass an absolute, stable path to --receipt and confirm the basename matches what the browser will report.
- Check `upload.file_names` in debug output and compare against `receiptBasename(input.receipt)` to see what actually got uploaded.
- Ensure the Browser Bridge runs on the same host/filesystem as the CLI so the exact file is accessible and uploaded unrenamed.
- If Mercury/bridge sanitizes filenames, relax the comparison to a normalized match in utils.js (lowercase, trim).
Example fix
// before opencli mercury reimbursement-draft --receipt receipt.png ... // after opencli mercury reimbursement-draft --receipt /abs/path/receipt.png ...
Defensive patterns
Strategy: validation
Validate before calling
import path from 'node:path';
import fs from 'node:fs';
const abs = path.resolve(input.receipt);
if (!fs.existsSync(abs)) throw new Error(`receipt not found: ${abs}`);
const expected = path.basename(abs);
// after upload: if (!upload.file_names?.includes(expected)) throw ... Type guard
function uploadedExpectedFile(upload, receiptPath) {
const names = Array.isArray(upload?.file_names) ? upload.file_names : [];
return names.includes(path.basename(receiptPath));
} Try / catch
try {
await uploadReceipt(page, selector, receiptPath);
} catch (e) {
if (String(e.message).includes('did not confirm the expected receipt file')) {
// compare upload.file_names vs basename(receiptPath), fix path, retry
} else throw e;
} Prevention
- Pass absolute receipt paths so the bridge uploads the exact file.
- Avoid symlinks/temp files with volatile names for receipts.
- Ensure the Browser Bridge daemon shares the same filesystem as the CLI.
- Log upload.file_names in debug runs to catch renaming early.
When it happens
Trigger: Running reimbursement-draft where the browser normalizes/renames the uploaded file, the receipt path contains characters the bridge mangles, or uploadFiles attached a different/cached file than the one given (e.g. path resolution mismatch between CLI cwd and browser bridge host).
Common situations: Receipt passed via relative path while the Browser Bridge daemon runs from a different working directory so it uploads a same-named-but-different file; symlinked or temp-renamed receipt (e.g. /tmp/receipt.png vs uploaded name); OS auto-renaming duplicates like receipt (1).png.
Related errors
- Mercury reimbursement-draft requires Browser Bridge uploadFi
- Mercury receipt upload did not confirm exactly one uploaded
- Mercury receipt upload did not confirm the intended receipt
- Failed to attach file
- Instagram fallback file injection failed
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/d4ee3bb7578afb43.
Report an issue: GitHub.