jackwener/OpenCLI · error · CommandExecutionError
Mercury reimbursement form fields were not all filled: ${mis
Error message
Mercury reimbursement form fields were not all filled: ${missing.join(', ')} What it means
Thrown when `fillReimbursementFields` reports that one or more of the six required expense fields (amount, currency, date, merchant, category, notes) were not marked as touched — i.e. the command could not confirm it actually set every field in the Mercury reimbursement form. The message lists the missing field keys. This prevents advancing to Review with a partially filled draft.
Source
Thrown at clis/mercury/reimbursement-draft.js:81
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;
});
const el = candidates.find((node) => norm(node.innerText || node.textContent || '') === 'review');
if (!el) return { clicked: false };
el.click();
return { clicked: true, text: el.innerText || el.textContent || '' };
})()`);
if (!reviewClick || typeof reviewClick !== 'object' || typeof reviewClick.clicked !== 'boolean') {
throw new CommandExecutionError('Mercury returned malformed Review click result');
}
if (!reviewClick.clicked) {View on GitHub (pinned to 49907e53dc)
Solutions
- Increase --ocr-wait-seconds (default 8) so Mercury's OCR finishes before/after field filling doesn't overwrite values.
- Inspect the listed missing field in the Mercury dialog DOM and update its selector/logic in fillReimbursementFields (clis/mercury/utils.js).
- Verify the CLI argument values are non-empty and valid (date as YYYY-MM-DD, amount numeric, category matching a Mercury option).
- Check fillReimbursementFields still records fields.touched[key] = true for every successfully filled field after any refactor.
Example fix
// before opencli mercury reimbursement-draft --receipt r.png --amount 140.00 --date 2026-06-26 --merchant "M" --notes "n" // after (raise OCR wait and supply explicit currency/category) opencli mercury reimbursement-draft --receipt r.png --amount 140.00 --currency CNY --date 2026-06-26 --merchant "M" --category "Marketing & Advertising" --notes "n" --ocr-wait-seconds 15
Defensive patterns
Strategy: validation
Validate before calling
const required = ['receipt','amount','currency','date','merchant','category','notes'];
for (const k of required) {
if (!kwargs[k] || String(kwargs[k]).trim() === '') {
throw new Error(`missing required arg: ${k}`);
}
}
if (!/^\d{4}-\d{2}-\d{2}$/.test(kwargs.date)) {
throw new Error('date must be YYYY-MM-DD');
} Type guard
function allFieldsTouched(fields, keys = ['amount','currency','date','merchant','category','notes']) {
return keys.every((k) => fields?.touched?.[k] === true);
} Try / catch
try {
await opencli(['mercury', 'reimbursement-draft', ...args]);
} catch (e) {
const m = String(e.message).match(/not all filled: (.+)/);
if (m) {
// retry with higher --ocr-wait-seconds for fields: m[1]
} else throw e;
} Prevention
- Supply all field arguments explicitly rather than relying on defaults/OCR.
- Raise --ocr-wait-seconds when receipts trigger Mercury's OCR rewrite.
- Keep fillReimbursementFields selectors current with Mercury's markup.
- Confirm category strings match Mercury's option labels exactly.
When it happens
Trigger: Running reimbursement-draft when a field's selector no longer matches after a Mercury UI change, OCR of the uploaded receipt overwrites fields after fill (race with input.ocrWaitSeconds too short), a field is disabled/hidden (e.g. currency locked to a default), or input coercion produces an empty value that the fill helper skips.
Common situations: ocr-wait-seconds too low so Mercury's OCR pass rewrites amount/merchant/notes after the CLI fills them; category string not matching any Mercury option so the select never updates; date format rejected by the custom date picker; field keys missing from fillReimbursementFields' touched tracking after a utils.js refactor.
Related errors
- 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
- Mercury receipt upload did not confirm the intended receipt
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c5148d59e17fc404.
Report an issue: GitHub.